java設計模式學習筆記(三) --- 行為型模式
文章目錄
責任鏈模式
我獲取了一個物件,現在需要根據物件內部的特徵來把它交給特定的類去處理。下面是一種簡單的實現方法。
package blog.java.pattern.chain;
public class ChainTest {
public static void main(String[] args) {
Obj obj = new Obj((int)(Math.random() * 2));
IInterface impl;
if(obj.flag == 0){//根據flag的值來確定具體由哪個實現類來處理。
impl = new Impl_1();
}else {
impl = new Impl_2();
}
impl.doSomething(obj);
}
}
class Obj{
int flag;
public Obj(int flag) {
this.flag = flag;
}
}
interface IInterface{public void doSomething(Obj obj);}
class Impl_1 implements IInterface{
public void doSomething(Obj obj) { System.out.println(1 + " " + obj); }
}
class Impl_2 implements IInterface{
public void doSomething(Obj obj) { System.out.println(2 + " " + obj); }
}
這麼寫可以解決問題,但是有缺陷。
作為客戶端,我不想判斷到底是誰去處理,這應該在別的地方判斷。
在實際問題中,判斷可能遠不止obj.flag == 0
責任鏈模式為這種問題提供了一種解決方法:讓處理類通過持有物件的方式,鏈式的判斷是否由自己處理。
package blog.java.pattern.chain;
public class ChainTest {
public static void main(String[] args) {
Obj obj = new Obj((int)(Math.random() * 2));
IInterface impl1 = new Impl_1();
IInterface impl2 = new Impl_2();
impl1.setNextImpl(impl2);
impl1.doSomething(obj);
}
}
class Obj{
int flag;
public Obj(int flag) {
this.flag = flag;
}
}
interface IInterface{
public void doSomething(Obj obj);
public void setNextImpl(IInterface nextImpl);
}
abstract class BaseImpl implements IInterface{
IInterface nextImpl;
public void setNextImpl(IInterface nextImpl) {
this.nextImpl = nextImpl;
}
}
class Impl_1 extends BaseImpl implements IInterface{
public void doSomething(Obj obj) {
if(obj.flag == 0)
System.out.println(1 + " " + obj);
else
super.nextImpl.doSomething(obj);
}
}
class Impl_2 extends BaseImpl implements IInterface{
public void doSomething(Obj obj) {
if(obj.flag == 1)
System.out.println(2 + " " + obj);
else
super.nextImpl.doSomething(obj);
}
}
策略模式
將用介面組織的一套方法,通過另一個類間接呼叫。
package blog.java.pattern.strategy;
public class StrategyTest {
public static void main(String[] args) {
Invoker invoker = new Invoker(new Strategy_1());
invoker.execute();
invoker = new Invoker(new Strategy_2());
invoker.execute();
}
}
interface IStrategy{public void doSomething();}
class Strategy_1 implements IStrategy{public void doSomething(){System.out.println("Strategy_1");}}
class Strategy_2 implements IStrategy{public void doSomething(){System.out.println("Strategy_2");}}
class Strategy_3 implements IStrategy{public void doSomething(){System.out.println("Strategy_3");}}
class Invoker{
IStrategy strategy;
public Invoker(IStrategy strategy) {
this.strategy = strategy;
}
public void execute(){
this.strategy.doSomething();
}
}
很樸實的解耦,完全不需要解釋。
命令模式
我需要執行一些複雜的功能,它們可能需要一個或多個類進行一系列操作才能完成。於是我把它們封裝到了另一個類B中來執行。同時,我又不想直接操作類B,而是通過另一個類來間接執行。
package blog.java.pattern.command;
public class CommandTest {
public static void main(String[] args) {
Invoker invoker = new Invoker();
invoker.setCommand(new ACommand());
invoker.execute();
}
}
class Receiver{ //執行具體操作
public void doA(){}
public void doB(){}
public void doC(){}
}
interface ICommand{public void execute();}
abstract class CommandFather implements ICommand{
Receiver receiver = new Receiver();
}
class ACommand extends CommandFather{//A命令
public void execute() {
this.receiver.doA();
}
}
class BCommand extends CommandFather{//B命令
public void execute() {
this.receiver.doB();
this.receiver.doC();
}
}
class Invoker{//客戶端直接呼叫的物件,主要為了將客戶端與命令類分開
private ICommand command;
public void setCommand(ICommand command) {
this.command = command;
}
public void execute(){
this.command.execute();
}
}
直譯器模式
簡單來說,就用面向物件的方式去解析語言。
應用面很窄,替代品很多,又解決不了真正複雜的問題…
用得到的時候再看吧。
迭代器模式
迭代器模式規定,一個集合類,需要在不暴露內部儲存結構的前提下,提供一個順序遍歷自身資料的方法。
這個模式幾乎不會在我們的程式碼中出現,不是因為沒用,而是太有用了。java所有的集合類都已經實現了這個方法,不需要我們去寫了。
個人覺得,學這個設計模式的最好方法就是直接看jdk原始碼,那裡有優秀的,經過實踐檢驗的示例。
這是我之前寫的ArrayList的迭代器模式的實現。ArrayList的底層是靜態陣列,它的迭代器是最簡單的,可以作為參考。
通過ArrayList原始碼深入理解java中Iterator迭代器的實現原理
中介者模式
若一組類之間存在多對多的關係(即每個類若想要實現某個方法,可能會呼叫多個其他類),則它們之間互稱為同事類。在同事類中存在這種關係的方法中,會有很多呼叫其它同事類的程式碼。將同事類中這些相互呼叫的程式碼封裝起來,就是中介者模式。
實現方法簡單描述一下就是:中介者類持有全部的同事類物件;同事類持有一箇中介者物件,並通過中介者物件來間接實現方法。
package blog.java.pattern.mediator;
public class MediatorTest {
public static void main(String[] args) {
Colleague_1 c1 = new Colleague_1();
Colleague_2 c2 = new Colleague_2();
Colleague_3 c3 = new Colleague_3();
Mediator mediator = new Mediator();
mediator.setC1(c1);
mediator.setC2(c2);
mediator.setC3(c3);
IMediator iMediator = mediator;
c1.setiMediator(iMediator);
c2.setiMediator(iMediator);
c3.setiMediator(iMediator);
c1.Colleague_1_method1(); //呼叫
c2.Colleague_2_method1(); //呼叫
}
}
interface IMediator{
public void method1();
public void method2();
}
abstract class AbstractMediator implements IMediator{
protected Colleague_1 c1;
protected Colleague_2 c2;
protected Colleague_3 c3;
public void setC1(Colleague_1 c1) {
this.c1 = c1;
}
public void setC2(Colleague_2 c2) {
this.c2 = c2;
}
public void setC3(Colleague_3 c3) {
this.c3 = c3;
}
}
//中介者
class Mediator extends AbstractMediator{
@Override
public void method1() {
super.c1.Colleague_1_method2();
super.c2.Colleague_2_method1();
}
@Override
public void method2() {
super.c3.Colleague_3_method1();
super.c1.Colleague_1_method3();
}
}
//同事類
class Colleague_1{
private IMediator iMediator;
public void setiMediator(IMediator iMediator) {
this.iMediator = iMediator;
}
public void Colleague_1_method1(){
this.iMediator.method1();
}
public void Colleague_1_method2(){}
public void Colleague_1_method3(){}
}
class Colleague_2{
private IMediator iMediator;
public void setiMediator(IMediator iMediator) {
this.iMediator = iMediator;
}
public void Colleague_2_method1(){
this.iMediator.method2();
}
}
class Colleague_3{
private IMediator iMediator;
public void setiMediator(IMediator iMediator) {
this.iMediator = iMediator;
}
public void Colleague_3_method1(){}
}
垃圾程式碼的形成往往是,錯誤的人,通過錯誤的方法,使用了錯誤的設計模式。中介者模式很可能三者全佔。
中介者模式的一大特點是,將原本同事類中複雜的程式碼,以更復雜的方式寫在了中介者類中。中介者類設計的失敗很可能會成為專案中的毒瘤。而且,若同事類中的邏輯本就不復雜,也是不太需要使用設計模式的。
不過若是使用得合適的話,確實可以使類的結構更清晰。它會使每一個同事類只與中介者類產生耦合關係。
中介者模式的一個核心思想是,將類間的控制提取出來,單獨封裝。這種思想或許比那些模板式的實現方法更有價值,MVC中的C正式用的這種思想。
備忘錄模式
簡單來說,就是存檔/讀檔。將當前類的部分內容存檔,並在需要的時候將類恢復到原來的狀態。
具體的實現方法會根據需求的不同而有較大的變化。簡單描述的一下的話,就是除了原本的需要備份的類外,還需要一個用於存檔的類與一個管理存檔的類。簡單實現一下。
package blog.java.pattern.memento;
public class MementoTest {
public static void main(String[] args) {
Originator originator = new Originator();
originator.setProp1("1");
System.out.println(originator.getProp1());
originator.createMemento(); //存檔
originator.setProp1("2");
System.out.println(originator.getProp1());
originator.restoreMemento(); //讀檔
System.out.println(originator.getProp1());
}
}
class Originator{
private String prop1;
private Caretaker caretaker = new Caretaker();
public void createMemento(){
Memento memento = new Memento();
memento.setProp1(this.prop1);
this.caretaker.setMemento(memento);
}
public void restoreMemento(){
Memento memento = this.caretaker.getMemento();
this.prop1 = memento.getProp1();
}
public String getProp1() {
return prop1;
}
public void setProp1(String prop1) {
this.prop1 = prop1;
}
private class Memento{
private String prop1;
public String getProp1() {
return prop1;
}
public void setProp1(String prop1) {
this.prop1 = prop1;
}
}
private class Caretaker{
private Memento memento;
public void setMemento(Memento memento){
this.memento = memento;
}
public Memento getMemento() {
return memento;
}
}
}
與其他大多數示例不同的是,我將管理者類也寫成內部類了。這可能有些不符合單一職責原則,但是我更喜歡用可自我維護的類。這樣在呼叫的時候,我只需要關注這一個類。
對於更復雜的情況,比如需要備份的內容更多,更復雜,或者需要多個備份,只要按這個模板稍稍改下就行了。
觀察者模式
(被觀察者)執行某些操作後,想要立即呼叫其他一組類(觀察者)的方法。這時考慮使用觀察者模式。(總覺得設計模式從我嘴裡說出來後,都low了好多…)
按照這句話的需求,很容易就能寫出這樣的程式碼。
package blog.java.pattern.observer;
import java.util.ArrayList;
import java.util.List;
public class ObserverTest {
public static void main(String[] args) {
MyObservable observable = new MyObservable();
observable.method();
}
}
//被觀察者
class MyObservable{
List<IMyObserver> list = new ArrayList<IMyObserver>();
{
list.add(new MyObserver1());
list.add(new MyObserver2());
}
public void method(){
this.notifyOb();
}
private void notifyOb(){
for(IMyObserver iMyObserver: list){
iMyObserver.update(this);
}
}
}
interface IMyObserver{
public void update(Object obj);
}
//觀察者
class MyObserver1 implements IMyObserver{
public void update(Object obj) {
System.out.println("1 " + obj);
}
}
class MyObserver2 implements IMyObserver{
public void update(Object obj) {
System.out.println("2 " + obj);
}
}
這個應該算是觀察者模式最簡陋的實現了。想寫的正規些的話,可以直接使用util包中準備好的類。jdk從1.0版本就已經提供觀察者介面與被觀察者類了。
java.util.Observable
java.util.Observer
原始碼非常簡單,按照上面的套路,稍稍改下就能用。
主要的改變是:增加了被觀察者中對觀察者的維護方法;被觀察者狀態變化的維護;一些必要的同步。
參考程式碼
package blog.java.pattern.observer.better;
import java.util.Observable;
import java.util.Observer;
public class ObserverTest2 {
public static void main(String[] args) {
MyObservable observable = new MyObservable();
observable.addObserver(new MyObserver2