责任链模式
我获取了一个对象,现在需要根据对象内部的特征来把它交给特定的类去处理。下面是一种简单的实现方法。
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);
}
}
由于被访问者类与访问者类的高度解耦,使得访问者模式具有非常高的灵活性。这两者可以在完全不污染对方结构的前提下进行扩展。缺点也很明显,被访问者需要暴露部分内部信息给访问者类。