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());
		observable.addObserver(new MyObserver1());
		
		observable.method();
	}
}

class MyObservable extends Observable{
	public void method(){
		super.setChanged();
		super.notifyObservers();
	}
}

class MyObserver1 implements Observer{
	public void update(Observable o, Object arg) {
		System.out.println("1 " + o);
	}
}

class MyObserver2 implements Observer{
	public void update(Observable o, Object arg) {
		System.out.println("2 " + o);
	}
}

没有乱七八糟的东西,因为jdk已经为我们准备好了通用的代码。

状态模式

有一个类,在调用其中的方法时,我需要根据这个类的内部状态来选择具体的实现类。一种直观的写法是这样的。

package blog.java.pattern.state.worse;

public class StateTest {
	
	public static void main(String[] args) {
		Context context = new Context();
		context.method1();
		context.method2();
	}
}

class Context{
	private int state = 1;
	
	public void method1(){
		switch (state) {
		case 1:
			System.out.println("do method1 state 1");
			this.state = 2;
			break;
		case 2:
			System.out.println("do method1 state 2");
			break;
		default:
			break;
		}
	}
	
	public void method2(){
		switch (state) {
		case 1:
			System.out.println("do method2 state 1");
			break;
		case 2:
			System.out.println("do method2 state 2");
			break;
		default:
			break;
		}
	}
}

状态模式对这种情况的改进方法是:将每一个状态封装为一个状态类,并且状态类允许维护状态。

package blog.java.pattern.state.better;

public class StateTest {

	public static void main(String[] args) {
		Context context = new Context();
		context.method1();
		context.method2();
	}

}

class Context{
	private IState iState1 = new State1(this);
	private IState iState2 = new State2(this);
	private IState iState = iState1;
	
	public void setIState(IState state) {
		this.iState = state;
	}

	public IState getiState1() {
		return iState1;
	}

	public IState getiState2() {
		return iState2;
	}

	public void method1(){
		this.iState.method1();
	}
	
	public void method2(){
		this.iState.method2();
	}
}

interface IState{
	public void method1();
	public void method2();
}

abstract class SuperState implements IState{
	protected Context context;

	public SuperState(Context context) {
		super();
		this.context = context;
	}
	
}

class State1 extends SuperState{

	public State1(Context context) {
		super(context);
	}

	public void method1() {
		super.context.setIState(super.context.getiState2());
		System.out.println("do method1 state 1");
	}

	public void method2() {
		System.out.println("do method2 state 1");
	}
}

class State2 extends SuperState{

	public State2(Context context) {
		super(context);
	}

	public void method1() {
		System.out.println("do method1 state 2");
	}

	public void method2() {
		System.out.println("do method2 state 2");
	}
}

改变内部的状态类这样的套路,跟策略模式很像,只是状态模式将策略的变更逻辑换到了内部执行。而根据状态的不同来调用不同的类,其实也是责任链模式的解决范畴。不过在责任链模式的定义中,没对是否改变上下文的状态做出过要求。仅从实现方式上看的话,这两个模式在很大程度上可以互换。

模板方法模式

在完成某个复杂的功能时,经常要按照严格的顺序执行一套操作。这个顺序并不是我们需要关心的,于是就把这个顺序预写到父类中,并在子类中实现具体的操作。最终通过父类调用子类方法的形式执行。

package blog.java.pattern.template;

public class TemplateTest {

	public static void main(String[] args) {
		MyInterface father = new Son();
		father.execute();
	}
}

interface MyInterface{
	public void execute();
}

abstract class Father implements MyInterface{
	public void execute(){
		this.method1();
		this.method1();
		this.method2();
		this.method3();
		this.method4();
	}
	
	abstract protected void method1();
	abstract protected void method2();
	abstract protected void method3();
	abstract protected void method4();
}

class Son extends Father{

	protected void method1() {
		System.out.println(1);
	}

	protected void method2() {
		System.out.println(2);
	}

	protected void method3() {
		System.out.println(3);
	}

	protected void method4() {
		System.out.println(4);
	}
	
}

即使从没学过设计模式也能熟练使用的套路,在项目中使用频率非常高。

访问者模式

又是一个策略模式的变种。区别在于,策略对象的持有者由类本身变成了具体的方法。简单来说,就是方法定义了接口类型的参数,并且这个接口参数需要接受类本身的一些信息。

package blog.java.pattern.visitor;

public class VisitorTest {

	public static void main(String[] args) {
		Interviewee interviewee = new Interviewee();
		interviewee.method(new Visitor1());
		interviewee.method(new Visitor2());
	}
}

class Interviewee{//被访问者类
	public void method(IVisitor iVisitor){
		iVisitor.visitorMethod(this);
	}
}

interface IVisitor{//访问者接口
	public void visitorMethod(Interviewee interviewee);
}

class Visitor1 implements IVisitor{//访问者1
	public void visitorMethod(Interviewee interviewee) {
		System.out.println("1 " + interviewee);
	}
}

class Visitor2 implements IVisitor{//访问者2
	public void visitorMethod(Interviewee interviewee) {
		System.out.println("2 " + interviewee);
	}
}

由于被访问者类与访问者类的高度解耦,使得访问者模式具有非常高的灵活性。这两者可以在完全不污染对方结构的前提下进行扩展。缺点也很明显,被访问者需要暴露部分内部信息给访问者类。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值