1. 程式人生 > >Java:GOF的23種設計模式(上)

Java:GOF的23種設計模式(上)

    最近學習設計模式,主要參考兩本書《大話設計模式》和《Java設計模式》,而兩本書的重要參考書目都有大名鼎鼎的GOF的《設計模式》。等到自己的理解能力足夠時,一定會繼續研讀《設計模式》、《Head First設計模式》這些聖經。現在的學習就當是一種瞭解,雖知現在學習不可能很深入,理解得非常淺顯,就當是為以後做個鋪墊,某年某月某日看到某些程式碼時,會驚覺這怎麼這麼像之前學的那些設計模式的?我想那時一定會有深刻的印象,從而瞭解到精髓,這就達到了學習的目的了。知識不能等到要用時才真正去學,應該不斷積累。

    這篇文章主要是集中了23種模式的程式碼Demo,而不會有太多的講解,因為大家學習都肯定是先從看書過來的,有了最初步的認識的。這些程式碼,有些是從《大話設計模式》(C#語言)一書中改寫過來的;有些是從網上看到一些比較好的例子然後模仿寫下來的;有些則是自己寫的。這篇文章還有這些程式碼當是自己的又一次複習和筆記整理吧(整個學習才接近一個禮拜,所以不可能會深入的)

    現在正式開始,每個例項程式碼前面只會有幾句自己看書時做的小筆記(有些是意思一樣的,只是摘錄的地方不同),按照《Java設計模式》的章節順序來寫。

目錄:

一:介面型模式  1)介面卡模式;2)外觀模式;3)組合模式;4)橋接模式 

二:責任型模式  1)單例模式;2)觀察者模式;3)中介者模式;4)代理模式;5)責任鏈模式;6)享元模式

三:構造型模式  1)建造者模式;2)工廠方法模式;3)抽象工廠模式;4)原型模式;5)備忘錄模式

四:操作型模式  1)模板方法模式;2)狀態模式;3)策略模式;4)命令模式;5)直譯器模式

五:拓展型模式  1)裝飾器模式;2)迭代器模式;3)訪問者模式

一:介面型模式

(1)介面卡模式(Adapter)

1.介面卡模式的宗旨是,保留現有類提供的服務,向客戶提供介面,以滿足客戶的期望。
2.使用一個已經存在的類,如果它的介面也就是方法和你的需求不同時,可以考慮用介面卡模式。

①Target類,客戶期待的介面

public class Target {
	public void request() {
		System.out.println("歐美家庭使用110v電壓");
	}
}
②Adaptee類,需要適配的類
public class Adaptee {
	public void specialRequest() {
		System.out.println("中國家庭使用110v電壓");
	}
}
③Adapter類,把客戶介面轉換成目標介面
public class Adapter extends Target{
	private Adaptee adaptee = new Adaptee(); //封裝一個Adaptee物件
	public void request() {
		adaptee.specialRequest();
	}
}
④Test類
public class Test {
	public static void main(String[] args) {
		// TODO 自動生成的方法存根
		Target target = new Adapter(); //通過介面卡,此時呼叫的是適配的類的方法
		target.request();
	}
}

(2)外觀模式(Facade)

1.外觀模式的目的在於提供一個介面,使子系統更加容易使用
2.為子系統的一組介面提供一個一致的介面,此模式定義了一個高層介面,這個介面使得這一子系統更加容易使用
3.外觀模式為設計粗糙或高度複雜的遺留程式碼(即子系統)提供一個簡單的介面,使新系統與Facade物件互動。

①一個步驟介面

public abstract class Step {
	public abstract void step();
}

②三個子系統類

public class StepOne extends Step {
	public void step() {
		System.out.println("手伸入袋子");
	}
}
public class StepTwo extends Step{
	public void step() {
		System.out.println("掏出鑰匙");
	}
}
public class StepThree extends Step {
	public void step() {
		System.out.println("開啟門");
	}
}
③外觀類,對子系統的屬性或方法進行組合,以備外界呼叫
public class Facade {
	private Step step1 = new StepOne();
	private Step step2 = new StepTwo();
	private Step step3 = new StepThree();
	public void step() {
		step1.step();
		step2.step();
		step3.step();
	}
}
④Test類
public class Test {
	public static void main(String[] args) {
		// TODO 自動生成的方法存根
		// Step step1 = new StepOne();
		// Step step2 = new StepTwo();
		// Step step3 = new StepThree();
		// step1.step();
		// step2.step();
		// step3.step();
		Facade facade = new Facade(); //通過外觀類的組裝,使子系統更加容易使用
		facade.step();
	}
}

(3)組合模式(Composite)

1.組合模式的設計意圖在於:讓使用者能夠用統一的介面處理單個物件以及物件組合
2.將物件組合成樹形結構以表示“部分-整體”的層次結構。組合模式使得使用者對單個物件和組合物件使用具有一致性
3.需求中是體現部分與整體層次結構,希望使用者可以忽略組合物件與單個物件的不同,統一地使用組合結構中的所有物件。

①Component介面

public abstract class Component {
	protected String name;
	public Component(String name) {
		this.name = name;
	}	
	public abstract void add(Component c);
	public abstract void remove(Component c);
	public abstract void display(int depth);
}
②Leaf類,表示單個物件
public class Leaf extends Component {
	public Leaf(String name) {
		super(name);
		// TODO 自動生成的建構函式存根
	}
	@Override
	public void add(Component c) {
		// TODO 自動生成的方法存根
	}
	@Override
	public void remove(Component c) {
		// TODO 自動生成的方法存根
	}
	@Override
	public void display(int depth) {
		// TODO 自動生成的方法存根
		for (int i = 0; i < depth; i++) {
			System.out.print("-");
		}
		System.out.println(name);
	}

}
③Composite類,表示物件組合
import java.util.ArrayList;
import java.util.List;
public class Composite extends Component {
	private List<Component> components = new ArrayList<Component>();
	public Composite(String name) {
		super(name);
		// TODO 自動生成的建構函式存根
	}
	@Override
	public void add(Component c) {
		// TODO 自動生成的方法存根
		components.add(c);
	}
	@Override
	public void remove(Component c) {
		// TODO 自動生成的方法存根
		components.remove(c);
	}
	@Override
	public void display(int depth) {      //區分單個物件和組合物件
		// TODO 自動生成的方法存根
		for (int i = 0; i < depth; i++) {
			System.out.print("-");
		}
		System.out.println(name);
		for (Component component : components) {     
			component.display(depth + 2);
		}
	}

}
④Test類
public class Test {
	public static void main(String[] args) {
		// TODO 自動生成的方法存根
		Composite root = new Composite("root");
		root.add(new Leaf("leaf1"));	
		Composite branch1 = new Composite("branch1");
		branch1.add(new Leaf("leaf2"));
		Composite branch2 = new Composite("branch2");
		branch2.add(new Leaf("leaf3"));
		branch2.add(new Leaf("leaf3"));		
		branch1.add(branch2);
		root.add(branch1);
		root.display(1);
	}
}

列印結果:

-root
---leaf1
---branch1
-----leaf2
-----branch2
-------leaf3
-------leaf3

(4)橋接模式(Bridge)

1.橋接模式的意圖是將抽象與抽象方法的實現相互分離,這樣它們就可以獨自變化。
2.將抽象部分與它的實現部分分離,使它們可以獨自變化。
3.實現系統有多角度分類,每一種分類都可能變化,那麼就把這種多角度分離出來讓它們獨自變化,減少它們之間的耦合性。

假設一顆繼承樹如下:

--手機

----手機品牌A

------2G手機品牌A

------3G手機品牌A

------4G手機品牌A

----手機品牌B

------2G手機品牌B

......

上面這種模式如果每增加一個手機品牌或者出產一種新的5G、6G手機都會造成很高的耦合。使用橋接模式可以很好地解耦:

①品牌抽象類

package bridge;
public abstract class Brand {
	protected XGPhone xgPhone; // key
	public Brand(XGPhone xgPhone) {
		this.xgPhone = xgPhone;
	}
	public abstract void run();
}
②手機模式抽象類
package bridge;
public abstract class XGPhone {
	public abstract void run();
}
③品牌繼承類
package bridge;
public class Brand_1 extends Brand {
	public Brand_1(XGPhone xgPhone) {
		super(xgPhone);
	}
	@Override
	public void run() {
		// TODO 自動生成的方法存根
		System.out.print(this);
		xgPhone.run();
	}
	public String toString() {
		return "品牌1的";
	}
}
package bridge;
public class Brand_2 extends Brand {
	public Brand_2(XGPhone xgPhone) {
		super(xgPhone);
	}
	@Override
	public void run() {
		// TODO 自動生成的方法存根
		System.out.print(this);
		xgPhone.run();
	}
	public String toString() {
		return "品牌2的";
	}
}
③手機模式繼承類
package bridge;
public class X2GPhone extends XGPhone {
	public void run(){
		System.out.println("2G手機");
	}
}
package bridge;
public class X3GPhone extends XGPhone {
	public void run(){
		System.out.println("3G手機");
	}
}
package bridge;
public class X4GPhone extends XGPhone {
	public void run(){
		System.out.println("4G手機");
	}
}
④Test類
package bridge;
public class Test {
	public static void main(String[] args) {
		// TODO 自動生成的方法存根
		Brand brand1 = new Brand_1(new X2GPhone());
		brand1.run();
	}

}

二:責任性模式

(1)單例模式(Singleton)

1.單例模式的宗旨在於確保某個類只有一個例項,並且為之提供一個全域性訪問點。
2.通過隱藏構造器和提供對建立物件的單個訪問點,單例模式就能夠將類的職責集中於類的某個單個例項中。

①Singleton類

public class Singleton {
	private static Singleton instance;
	private static Object lock = Singleton.class;
	private Singleton() { // 將構造器設為私有

	}
	// 獲得類例項的唯一全域性訪問點
	public static Singleton getInstance() {
		// 確保只有一個例項
		synchronized (lock) {
			if (instance != null) {
				instance = new Singleton();
			}
			return instance;
		}
	}
}
②Test類
public class Test {
	public static void main(String[] args) {
		Singleton s1 = Singleton.getInstance();
		Singleton s2 = Singleton.getInstance();
		System.out.println(s1 == s2);
	}
}

(2)觀察者模式(Observer)

1.觀察者模式的宗旨是在多個物件之間定義一對多的關係,以便當一個物件狀態改變的時候,其他所有依賴於這個物件的物件都能夠得到通知,並自動更新。
2.觀察者模式又叫做釋出-訂閱模式,定義了一種一對多的依賴關係,讓多個觀察者物件同時監聽某一個主題物件。這個主題物件在狀態發生變化時,會通知所有觀察者物件,使它們能夠自動更新自己。

①抽象通知者

public abstract class Notifier {
	public abstract void attach(Observer observer);
	public abstract void detach(Observer observer);
	public abstract void notifyObservers();
}
②抽象觀察者
public abstract class Observer {
	protected String name;
	public abstract void update();
	public Observer(String name) {
		this.name = name;
	}
}
③具體通知者
import java.util.ArrayList;
import java.util.List;
public class SecretaryNotifier extends Notifier {
	private List<Observer> observers = new ArrayList<Observer>();
	@Override
	public void attach(Observer observer) {
		// TODO 自動生成的方法存根
		observers.add(observer);
	}
	@Override
	public void detach(Observer observer) {
		// TODO 自動生成的方法存根
		observers.remove(observer);
	}
	@Override
	public void notifyObservers() { 
		// TODO 自動生成的方法存根
		for (Observer observer : observers) {
			System.out.print("祕書說:");
			observer.update();
		}
	}

}
④具體觀察者
public class NBAObserver extends Observer {
	public NBAObserver(String name) {
		super(name);
		// TODO 自動生成的建構函式存根
	}
	@Override
	public void update() {
		// TODO 自動生成的方法存根
		System.out.println(name + ",老闆回來了,別看NBA了");
	}
}
public class SleepObserver extends Observer {
	public SleepObserver(String name) {
		// TODO 自動生成的建構函式存根
		super(name);
	}
	@Override
	public void update() {
		// TODO 自動生成的方法存根
		System.out.println(name + ",老闆回來了,別睡了");
	}
}
④Test類
public class Test {
	public static void main(String[] args) {
		Notifier notifier = new SecretaryNotifier();
		Observer observer = new SleepObserver("小明");
		Observer observer2 = new SleepObserver("小東");
		Observer observer3 = new NBAObserver("小強");
		notifier.attach(observer);
		notifier.attach(observer2);
		notifier.attach(observer3);
		notifier.notifyObservers(); // 一次更新所有
	}
}
列印結果:
祕書說:小明,老闆回來了,別睡了
祕書說:小東,老闆回來了,別睡了
祕書說:小強,老闆回來了,別看NBA了

(3)中介者模式(Mediator)

1.觀察者模式通過儘可能縮小一個物件應對其他物件承擔的責任範圍來支援這種責任分配;單例模式將責任集中於其他物件都可以訪問和複用的某個特定物件中;與單例模式類似的是,中介者模式也對責任進行了集中,不過這種模式只是對某個特定物件集合的責任進行集中,而不是對整個系統的其他所有物件的責任進行集中。
2.中介者模式的意圖是定義一個物件,該物件將物件集合之間的互動封裝起來。利用該模式可以降低物件之間的耦合程度,避免物件之間的顯示引用,還可以讓物件間的互動獨立變化。
3.中介者模式,用一箇中介物件來封裝一系列的物件互動。中介者使各物件不需要顯示地相互引用,從而使耦合鬆散,而且可以獨立地改變他們之間的互動。

①中介者類,封裝需要互動的類

public class Mediator {
	private ConColleague1 cc1;
	private ConColleague2 cc2;
	public void setCC1(ConColleague1 cc1) {
		this.cc1 = cc1;
	}
	public void setCC2(ConColleague2 cc2) {
		this.cc2 = cc2;
	}
	public void sendMessage(String message, Colleague colleague) { //具體互動
		if (colleague == cc1) {
			cc2.getMessage(message);
		} else {
			cc1.getMessage(message);
		}
	}
}
②抽象同事類,它的子類是一些會產生互動的類
public abstract class Colleague {
	protected Mediator mediator;

	public Colleague(Mediator mediator) {
		this.mediator = mediator;
	}
}
③具體同事類
public class ConColleague1 extends Colleague {
	public ConColleague1(Mediator mediator) {
		super(mediator);
		// TODO 自動生成的建構函式存根
	}
	public void sendMessage(String message) {
		mediator.sendMessage(message, this);
	}
	public void getMessage(String message) {
		System.out.print("同事1得到的資訊:" + message);
	}
}
public class ConColleague2 extends Colleague {
	public ConColleague2(Mediator mediator) {
		super(mediator);
		// TODO 自動生成的建構函式存根
	}
	public void sendMessage(String message) {
		mediator.sendMessage(message, this);
	}
	public void getMessage(String message) {
		System.out.print("同事2得到的資訊:" + message);
	}
}
④Test類
public class Test {
	public static void main(String[] args) {
		// TODO 自動生成的方法存根
		Mediator mediator = new Mediator();
		// 認識相同的中介者,通訊封裝在中介者類中
		ConColleague1 cc1 = new ConColleague1(mediator);
		ConColleague2 cc2 = new ConColleague2(mediator);
		mediator.setCC1(cc1);
		mediator.setCC2(cc2);
		cc1.sendMessage("你好啊");
	}

}
(4)代理模式(Proxy)

1.代理模式的意圖在於為物件提供一個代理或者佔位來控制對該物件的訪問
2.代理模式,為其他物件提供一種代理以控制對這個物件的訪問

①Subject介面

public abstract class Subject {
	public abstract void request();
}
②RealSubject
public class RealSubject extends Subject {
	@Override
	public void request() {
		// TODO 自動生成的方法存根
		System.out.println("real request");
	}
}
③ProxySubject
public class ProxySubject extends Subject {
	RealSubject rs = new RealSubject();
	@Override
	public void request() {
		// TODO 自動生成的方法存根
		rs.request();
	}
}
④Test類
public class Test {
	public static void main(String[] args) {
		ProxySubject proxySubject = new ProxySubject();
		proxySubject.request(); // 真正的物件是rs,proxySubject起到一個代理作用
	}
}

(5)責任鏈模式(Chain Of Responsibility)

1.責任鏈模式可讓每個物件都有一次機會決定自己是否處理請求,以便於避免請求的傳送者與其接收者的耦合。
2.使多個物件都有機會處理請求,從而避免請求的傳送者和接收者之間的耦合關係。將這個物件連成一條鏈,並沿著這條鏈傳遞該請求,直到有一個物件可以處理它為止。

①抽象Handler類

public abstract class Handler {
	protected Handler leader;
	protected String name;
	public Handler(String name) {
		this.name = name;
	}
	public void setLeader(Handler leader) { // key
		this.leader = leader;
	}

	public abstract void handleRequest(String requestType);
}
②具體Handler類
public class ConcreteHandler_1 extends Handler {
	public ConcreteHandler_1(String name) {
		super(name);
		// TODO 自動生成的建構函式存根
	}
	@Override
	public void handleRequest(String requestType) {
		// TODO 自動生成的方法存根
		if (requestType == "請假") {
			System.out.println(name + "批准請假");
		} else if (leader != null) { // 如果無法處理,傳遞給上一級
			leader.handleRequest(requestType);
		}
	}
}
public class ConcreteHandler_2 extends Handler {
	public ConcreteHandler_2(String name) {
		super(name);
		// TODO 自動生成的建構函式存根
	}
	@Override
	public void handleRequest(String requestType) {
		// TODO 自動生成的方法存根
		if (requestType == "加薪") {
			System.out.println(name + "批准加薪");
		} else if (leader != null) {
			leader.handleRequest(requestType);
		}
	}

}
public class ConcreteHandler_3 extends Handler {
	public ConcreteHandler_3(String name) {
		super(name);
		// TODO 自動生成的建構函式存根
	}
	@Override
	public void handleRequest(String requestType) {
		// TODO 自動生成的方法存根
		if (requestType == "分紅") {
			System.out.println(name + "批准分紅");
		} else if (leader != null) {
			leader.handleRequest(requestType);
		}
	}

}
③Test類
public class Test {
	public static void main(String[] args) {
		ConcreteHandler_1 ch1 = new ConcreteHandler_1("老大李明");
		ConcreteHandler_2 ch2 = new ConcreteHandler_2("經理王強");
		ConcreteHandler_3 ch3 = new ConcreteHandler_3("老闆王五");
		ch1.setLeader(ch2);
		ch2.setLeader(ch3);
		ch1.handleRequest("請假");
		ch1.handleRequest("加薪");
		ch1.handleRequest("分紅");
	}
}
列印結果:
老大李明批准請假
經理王強批准加薪
老闆王五批准分紅

(6)享元模式(Flyweight)

1.享元模式主要意圖在於通過共享來支援大量的細粒度物件的使用效率
2.運用共享技術有效地支援大量細粒度的物件

①Flyweight介面

public abstract class Flyweight {
	public abstract void operation(int ex);
}
②繼承類,可以是需要共享的,也可以是不需要共享的。
public class ConcreteFlyweight extends Flyweight {
	@Override
	public void operation(int ex) {
		// TODO 自動生成的方法存根
		System.out.println("具體Flyweight" + ex);
	}

}
public class UnsharedFlyweight extends Flyweight {
	@Override
	public void operation(int ex) {
		// TODO 自動生成的方法存根
		System.out.println("不共享的具體Flyweight" + ex);
	}
}
③Flyweight工廠,達到享元效果
import java.util.HashMap;
import java.util.Map;
public class FlyweightFactory {
	private Map<String, Flyweight> flyweights = new HashMap<String, Flyweight>();
	public Flyweight getFlyweight(String key) {
		if (!flyweights.containsKey(key)) // 如果此前物件已經存在則返回,否則例項一個新的物件 
			flyweights.put(key, new ConcreteFlyweight());
		return flyweights.get(key);
	}
}
④Test類
public class Test {
	public static void main(String[] args) {
		FlyweightFactory ff = new FlyweightFactory();
		Flyweight fx = ff.getFlyweight("X");
		Flyweight fx1 = ff.getFlyweight("X");
		Flyweight fx2 = ff.getFlyweight("X");
		System.out.println(fx == fx1 && fx1 == fx2); //三個物件都是同一個物件
		Flyweight fy = ff.getFlyweight("Y");
		System.out.println(fx == fy);
		UnsharedFlyweight uf = new UnsharedFlyweight();
		System.out.println(fx == uf);
	}
}

列印結果:

true
false
false

未完待續......