1. 裝飾者模式
舉個栗子杰赛,假如在一家飲料店中有兩種飲料呢簸,分別是奶茶和咖啡,相對的有三種調(diào)料,蜂蜜根时、塊糖和摩卡瘦赫,這樣消費者就有不同的消費組合,比如加糖摩卡咖啡蛤迎、蜂蜜摩卡咖啡确虱,加糖奶茶......如果飲料的種類或者調(diào)料的種類增多,那么消費組合就會相應(yīng)的增多替裆,反映到編程代碼上就會出現(xiàn)“類爆炸”校辩,而且再添加新的飲料或者調(diào)料時會不可避免的改變原有的類的代碼,這就違反了設(shè)計原則中的開放-關(guān)閉原則辆童,即類應(yīng)該對擴(kuò)展開放宜咒,對修改關(guān)閉。
1.1 類圖
使用裝飾者模式就能很好地解決這個問題把鉴,廢話不多說故黑,該例子的類圖如下:
?
其中Beverage是抽象類,CondimentDecorator是繼承自Beverage類的抽象類庭砍。
1.2 飲料的抽象類
Beverage.java代碼:
/**
* @author yylin
*/
public abstract class Beverage {
// 飲料的描述
protected String description;
public Beverage() {
description = "飲料的抽象類";
}
public String getDescription() {
return description;
}
public abstract double cost();
}
1.3 調(diào)味品的抽象類
CondimentDecorator.java代碼:
/**
* @author yylin
*/
// 調(diào)味品裝飾者场晶,繼承自飲料的抽象類
public abstract class CondimentDecorator extends Beverage {
public CondimentDecorator() {
description = "調(diào)味品的抽象類";
}
@Override
public abstract String getDescription();//所有的調(diào)料品裝飾者必須重寫getDescription()方法
@Override
public abstract double cost();//所有的調(diào)料品裝飾者必須重寫cost()方法
}
1.4 飲料 奶茶的實現(xiàn)類
TeaMilk.java的代碼:
/**
* @author yylin
*/
public class TeaMilk extends Beverage {
public TeaMilk(){
//飲料的描述
description="奶茶";
}
/* (non-Javadoc)
* @see Beverage#cost()
*/
@Override
public double cost() {
//奶茶一杯三塊錢
return 3.0;
}
}
1.5 飲料 咖啡的實現(xiàn)類
Coffee.java的代碼:
/**
* @author yylin
*/
public class Coffee extends Beverage {
public Coffee() {
//飲料的描述
description="咖啡";
}
/* (non-Javadoc)
* @see Beverage#cost()
*/
@Override
public double cost() {
//咖啡一杯四塊錢
return 4.0;
}
}
1.6 調(diào)味品 蜂蜜的實現(xiàn)類
Honey.java的代碼:
/**
* @author yylin
*/
// 蜂蜜,繼承自調(diào)味品抽象類
public class Honey extends CondimentDecorator {
// 記錄飲料的變量逗威,是被裝飾者
Beverage beverage;
// 讓被裝飾者記錄到實例變量中
public Honey(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
// 描述調(diào)味品和飲料
return "蜂蜜" + beverage.getDescription();
}
/*
* (non-Javadoc)
*
* @see CondimentDecorator#cost()
*/
@Override
public double cost() {
// 加蜂蜜一塊錢峰搪,計算加了蜂蜜的飲料的價錢
return 1.0 + beverage.cost();
}
}
1.7 調(diào)味品 摩卡的實現(xiàn)類
Mocha.java的代碼:
/**
* @author yylin
*/
// 摩卡,繼承自調(diào)味品裝飾者
public class Mocha extends CondimentDecorator {
// 用一個變量記錄飲料凯旭,也就是被裝飾者
Beverage beverage;
// 把被裝飾者記錄到實例變量中
public Mocha(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
// 描述調(diào)味品和飲料
return "摩卡" + beverage.getDescription();
}
@Override
public double cost() {
// 加摩卡一塊錢概耻,計算加了摩卡的飲料的價錢
return 1.0 + beverage.cost();
}
}
1.8 調(diào)味品 糖的實現(xiàn)類
Sugar.java的代碼:
/**
*
* @author yylin
*
*/
// 糖,繼承自調(diào)味品抽象類
public class Sugar extends CondimentDecorator {
// 用一個變量記錄飲料罐呼,也就是被裝飾者
Beverage beverage;
// 把被裝飾者記錄到實例變量中
public Sugar(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
// 描述加糖的飲料
return "加糖" + beverage.getDescription();
}
@Override
public double cost() {
// 計算加了糖的飲料的價錢
return 1.0 + beverage.cost();
}
}
1.9 測試類
/**
* @author yylin
*
*/
//測試類
public class TestMain {
public static void main(String[] args) {
// 點一杯蜂蜜摩卡咖啡
Beverage beverage1 = new Coffee();// 定義咖啡對象
beverage1 = new Mocha(beverage1);// 用摩卡裝飾
beverage1 = new Honey(beverage1);// 用蜂蜜裝飾
System.out.println("顧客點了(" + beverage1.getDescription() + ")\n價格是:"
+ beverage1.cost() + "元");
// 點一杯加糖奶茶
Beverage beverage2 = new TeaMilk();// 定義奶茶對象
beverage2 = new Sugar(beverage2);// 用糖裝飾
System.out.println("顧客點了(" + beverage2.getDescription() + ")\n價格是:"
+ beverage2.cost() + "元");
}
}
運行結(jié)果
2. JAVA I/O中的裝飾者模式
例題:
先從文件test.txt中讀Employee對象的數(shù)據(jù)存到HashMap鞠柄,
再把HashMap中的Employee對象的數(shù)據(jù)存到一個新的文件test2.txt。
代碼:
2.1 Employee實體類Employee.java
package com.nwpu;
/**
*
* @author yylin
*
*/
public class Employee {
private String id;
private String name;
private String department;
public Employee() {
}
public Employee(String id, String name, String department) {
this.id = id;
this.name = name;
this.department = department;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
@Override
public String toString() {
return id+","+name+","+department;
}
}
2.2 測試類TestFileIO.java
package com.nwpu;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.HashSet;
import java.util.Set;
/**
*
* @author yylin
* 讀取文件時:
* 一行一行讀取文件嫉柴,解決讀取中文字符時出現(xiàn)亂碼厌杜。
* 流的關(guān)閉順序:先打開的后關(guān),后打開的先關(guān)计螺,
* 否則有可能出現(xiàn)java.io.IOException: Stream closed異常夯尽。
*
* 寫入文件時:
* 一行一行寫入文件,解決寫入中文字符時出現(xiàn)亂碼登馒。
* 流的關(guān)閉順序:先打開的后關(guān)匙握,后打開的先關(guān),
* 否則有可能出現(xiàn)java.io.IOException: Stream closed異常陈轿。
*/
public class TestFileIO {
public static void main(String[] args) {
/**
* read file
*/
FileInputStream fis=null;//文件輸入流
InputStreamReader isr=null;//讀入輸入流
BufferedReader br=null;//對讀入的文件流緩存
Set<Employee> set=new HashSet<Employee>();
try {
String fileURL="E:/test/test.txt";
fis=new FileInputStream(fileURL);
//解決讀入中文亂碼的問題 + 用InputStreamReader類裝飾FileInputStream類
isr=new InputStreamReader(fis,"UTF-8");
//用BufferedReader類裝飾BufferedReader類
br=new BufferedReader(isr);
String line="";
String arrs[]=null;
//按行讀入
while ((line=br.readLine())!=null) {
System.out.println(line);//輸出讀入的行
arrs=line.split(",");
//注入對象
set.add(new Employee(arrs[0],arrs[1],arrs[2]));
}
//流的關(guān)閉順序:先打開的后關(guān)圈纺,后打開的先關(guān)
br.close();
isr.close();
fis.close();
} catch (Exception e) {
e.printStackTrace();
}
for (Employee e : set) {
System.out.println(e.toString());
}
/**
* write file
*/
FileOutputStream fos=null;//文件輸出流
OutputStreamWriter osw=null;//寫出輸出流
BufferedWriter bw=null;//緩存寫出的輸出流
try {
String fileURL="E:/test/test2.txt";
fos=new FileOutputStream(fileURL);
//解決中文亂碼問題 + 用OutputStreamWriter類裝飾FileOutputStream類
osw=new OutputStreamWriter(fos,"UTF-8");
//用BufferedWriter類裝飾OutputStreamWriter類
bw=new BufferedWriter(osw);
for (Employee e : set) {
bw.write(e.toString()+"\r\n");
}
//注意關(guān)閉的先后順序秦忿,先打開的后關(guān)閉,后打開的先關(guān)閉
bw.close();
osw.close();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.3 運行結(jié)果
程序運行前:
test.txt中:
程序運行后:
test.txt中: