# DesignPatternsStudy **Repository Path**: LiuLinXi/DesignPatternsStudy ## Basic Information - **Project Name**: DesignPatternsStudy - **Description**: 大话设计模式 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2019-05-22 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 设计模式笔记 ## 1.简单工厂模式 - 使用一个单独的类去负责创建类的实例,这个单独的类就是一个工厂 ### 示例 ```java package pattern1; /** * 运算工厂 * * @author wdl */ public class OperationFactory { /** * 根据输入的符号创建不同的对象 * @param operate 输入运算符 * @return 返回创建的对象 */ public static AbstractOperation createOperation(String operate) { AbstractOperation operation = null; switch (operate) { case "+": operation = new OperationAdd(); break; case "-": operation = new OperationSub(); break; case "*": operation = new OperationMul(); break; case "/": operation = new OperationDiv(); break; default: } return operation; } } ``` ## 2.策略模式 - 定义了算法家族,分别封装起来,让它们之间可以相互转换,此模式让算法的变化,不会影响到用算法的客户 ### 示例 - 算法完成的工作相同,但是实现不同,可以以相同的方式调用不同的算法 - 策略模式的Strategy层为Context定义了一系列可以重用的算法或行为 - 通过Context控制算法的调用 ```java package pattern2; ///** // * @author wdl // */ //public class CashContext { // private CashSuper cashSuper; // // /** // * 通过构造方法传入具体的收费策略 // * @param cashSuper 收费策略 // */ // public CashContext(CashSuper cashSuper) { // this.cashSuper = cashSuper; // } // // /** // * 根据不同收费策略获得结构 // * @param money 输入金额 // * @return 返回具体收费策略的收费金额 // */ // public double getResult(double money) { // return cashSuper.acceptCash(money); // } //} /** * 结合简单工厂的Context类 * * @author wdl */ public class CashContext { private CashSuper cashSuper = null; public CashContext(int type) { switch (type) { //普通收费 case 1: cashSuper = new CashNormal(); break; //8折优惠 case 2: cashSuper = new CashRate(0.8); break; //满300-100 case 3: cashSuper = new CashReturn(300, 100); break; default: } } public double getResult(double money) { return cashSuper.acceptCash(money); } } ``` - 比较前面的工厂模式,工厂模式需要暴露两个类CashSuper与CashFactory,策略只需要暴露CashContext ## 3.装饰模式 - 动态的给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活 ### 示例 Component类 ```java public abstract class Component{ public abstract void opertion(); } ``` ConcreteComponent类 ```java public ConcreteComponent extents Component{ @overrive public void opertion(){ System.out.print("具体对象的具体操作"); }; } ``` Decorator类 继承Component,从外类对于Component进行功能扩展 ```java abstract class Decorator extents Component{ protected Component component; //设置Component public void setComponet(Component component){ this.component=component; } @overrive public void opertion(){ //如果被装饰对象Component不为空则调用被装饰Component的操作,即主要职责 if(component!=null){ component.opertion; } }; } ``` ConreteDecorator类 具体的装饰对象,给Component添加职责 ```java class ConreteDecoratorA extents Decorator{ private String state; @overrive public void opertion(){ //完成被装饰对象的主要职责 super.opertion(); //这里执行了装饰A特有的方法,相当于对于Component进行了装饰 state="new State"; addOpertion(); System.out.print("具体装饰对象A的具体操作"); }; public void addOpertion(){ //.... } } ``` 具体装饰过程 ```java ConcreteComponent c=new ConcreteComponent(); ConreteDecoratorA dA=new ConreteDecoratorA(); dA.setComponent(c); dA.opertion(); ``` - 装饰模式利用setComponent来对于被装饰对象进行封装。这样每个装饰对象的具体实现就和如何使用这个对象分离了,每个装饰对象只要关心自己的功能,不需要关心自己是如何被添加到对象链中的。 ### 优点 - 将类中的装饰功能从类中搬除,这样可以简化原有的类 - 有效的把类的核心职责和装饰功能区分开。而且可以去除相关类中重复的装饰功能。 ## 4.代理模式 - 代理模式是指为其它对象提供一种代理以控制对这个对象的访问 ### 示例 定义一个实体和代理共用的接口 ```java /** * @author wdl * 代理接口 */ public interface IGiveGift { /** * 送样娃娃 */ void giveDolls(); /** * 送花 */ void giveFollowers(); } ``` 实体的类定义代理代表的真实实体 ```java /** * 追求者 * * @author wdl */ public class PurSuit implements IGiveGift { SchoolGirl schoolGirl; public PurSuit(SchoolGirl schoolGirl) { this.schoolGirl = schoolGirl; } @Override public void giveDolls() { System.out.println(schoolGirl.getName() + "这是送你的洋娃娃"); } @Override public void giveFollowers() { System.out.println(schoolGirl.getName() + "这是送你的花"); } } ``` Proxy代理通过引用来使得代理可以访问实体,并且实现相同的接口,使得代理可以用来替代实体类 ```java /** * 代理 * 娇娇不认识追求者,但是可以通过代理得到追求者送的礼物 * * @author wdl */ public class Proxy implements IGiveGift { /** *实体类的引用 */ PurSuit purSuit; public Proxy(SchoolGirl schoolGirl) { purSuit = new PurSuit(schoolGirl); } /** * 在实际的方法中去调用”追求者类“中的方法 */ @Override public void giveDolls() { purSuit.giveDolls(); } @Override public void giveFollowers() { purSuit.giveFollowers(); } } ``` ### 适用场合 - 远程代理:为了一个对象在不同的地址空间提供局部代表。这样就可以隐藏一个对象存在于不同地址空间的事实; - 虚拟代理:根据需要创建开销很大的对象。通过它来存放实例化很长时间的真实对象; 例如:打开很大的HTML网页,里面有许多图片文字,但是你还是可以快速的打开,之后会看到网页的加载,就是虚拟代理存储了尺寸代替了真实的数据; - 安全代理:控制真实对象的访问权限; - 智能指引:当调用真实对象时,代理处理另外一些事情。 ## 5.工厂方法模式 - 比较前面的简单工厂模式,工厂模式为每一个运算创建一个工厂,而不是通过一个工厂控制 - 简单工厂模式最大的优点在于工厂类中包含必要的逻辑判断,可以根据客户端传入运算符动态选择实例化相关类,对于客户端来说,去除了与具体产品的依赖,但是需要增加时需要修改工厂类中的case条件,违背了对扩展开放对修改关闭的原则 - 工厂方法模式,定义了一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使得一个类的实例化延迟到其子类。 - 工厂方法中客户端需要决定实例化哪个工厂来实现运算 ### 示例 ```java /** * 各个实例的工厂 * @author wdl */ public class Factory { } /** * 加法工厂 */ class AddFactory implements IFactory{ @Override public AbstractOperation createOperation() { return new OperationAdd(); } } /** * 减法工厂 */ class SubFactory implements IFactory{ @Override public AbstractOperation createOperation() { return new OperationSub(); } } /** * 乘法工厂 */ class MulFactory implements IFactory{ @Override public AbstractOperation createOperation() { return new OperationMul(); } } /** * 除法工厂 */ class DivFactory implements IFactory{ @Override public AbstractOperation createOperation() { return new OperationDiv(); } } ``` 使用: ```java /** * 工厂方法模式 * @author wdl */ public class FactoryPattern { public static void main(String[] args) { IFactory operFactory=new AddFactory(); AbstractOperation addOperation=operFactory.createOperation(); addOperation.numberA=10; addOperation.numberB=20; System.out.println(addOperation.getResult()); } } ``` ## 6.原型模式 - 用原型实例创建对象的种类,并通过拷贝这些原型创建新的对象 - 原型模式其实就是从一个对象再创建另一个可定制的对象,而且不需要知道任何创建的细节 - java中可以直接实现*Cloneable* 接口来实现 ### 示例 ```java /** * 通过实现Cloneable接口来实现克隆 */ public class Resume implements Cloneable { private String name; private String company; public Resume(String name){ this.name=name; } public void setCompany(String company){ this.company=company; } public void displayCompany(){ System.out.println(company); } @Override protected Object clone() { Object clone = null; try { clone = super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return clone; } } ``` 使用: ```java /** * @author wdl */ public class ResumePrototype { public static void main(String[] args) { Resume resume=new Resume("小菜"); resume.setCompany("今日头条"); Resume resume1= (Resume) resume.clone(); resume1.setCompany("google"); Resume resume2= (Resume) resume.clone(); resume2.setCompany("BaiDu"); resume.displayCompany(); resume1.displayCompany(); resume2.displayCompany(); } } ``` ## 7.模板方法模式 - 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模版方法使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤 - 模板方法模式是通过把不变行为搬移到超类,去除子类中的重复代码来体现优势 - 模板方法模式提供了一个很好的代码复用的平台 - 当不变和变化的代码杂和在子类的实现中,不变的代码在子类中重复出现,可以通过模板方法模式把这些不变的行为搬移到单一的地方,这样帮助子类摆脱重复的不变行为的纠缠 ### 示例 抽象的模板方法实现骨架: ```java /** * 定义一个抽象的模板 * @author wdl */ public abstract class AbstractClass { /** * 将一些抽象的行为放到子类去实现 */ protected abstract void primitiveOperation1(); /** * 将一些抽象的行为放到子类去实现 */ protected abstract void primitiveOperation2(); /** * 模板方法,给出逻辑的骨架,而逻辑的组成是一些相应的抽象操作,它们被延迟到子类去实现 */ public final void templateMethod(){ System.out.println("开始"); primitiveOperation1(); primitiveOperation2(); System.out.println("完成"); } } ``` 子类实现具体的方法: ```java /** * @author wdl */ public class ConcreteClassA extends AbstractClass{ @Override protected void primitiveOperation1() { System.out.println("具体类A实现方法1"); } @Override protected void primitiveOperation2() { System.out.println("具体类A实现方法2"); } } /** * @author wdl */ public class ConcreteClassB extends AbstractClass { @Override protected void primitiveOperation1() { System.out.println("具体类B实现方法1"); } @Override protected void primitiveOperation2() { System.out.println("具体类B实现方法2"); } } ``` ```java /** * p90 * 模板方法模式 * @author wdl */ public class TemplateMethodPattern { public static void main(String[] args) { AbstractClass c; System.out.println("具体实现类A:"); c=new ConcreteClassA(); c.templateMethod(); System.out.println("\n具体实现类B:"); c=new ConcreteClassB(); c.templateMethod(); } } ``` ## 8.外观模式 - 为子系统的接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。 ### 示例 子系统: ```java /** * 子系统1 * @author wdl */ public class SubSystemOne { public void methodOne(){ System.out.println("子系统方法1"); } } /** * 子系统2 * @author wdl */ public class SubSystemTwo { public void methodTwo(){ System.out.println("子系统方法2"); } } /** * 子系统3 * @author wdl */ public class SubSystemThree { public void methodThree(){ System.out.println("子系统方法3"); } } ``` 外观类: ```java /** * 外观类 *

* 了解子系统的方法和属性,进行组合方便外部调用 * * @author wdl */ public class Facade { private SubSystemOne subSystemOne; private SubSystemTwo subSystemTwo; private SubSystemThree subSystemThree; public Facade() { subSystemOne = new SubSystemOne(); subSystemTwo = new SubSystemTwo(); subSystemThree = new SubSystemThree(); } public void methodA() { System.out.println("\n方法组合A"); subSystemOne.methodOne(); subSystemTwo.methodTwo(); subSystemThree.methodThree(); } public void methodB() { System.out.println("\n方法组合B"); subSystemThree.methodThree(); subSystemTwo.methodTwo(); } } ``` 客户端调用: ```java /** * p103 * 外观模式 * @author wdl */ public class FacadePattern { public static void main(String[] args) { Facade facade=new Facade(); facade.methodA(); facade.methodB(); } } ``` ### 使用场景 1. 设计初期阶段,应该将不同的层次分离,比如数据访问层、数据层、业务逻辑层等,层与层之间建立外观模式 2. 开发阶段,子系统往往因为不断的重构演化而变得复杂,增加外观模式Facade可以提供一个简单的接口,减少之间的依赖 3. 在维护一个大型的遗留项目时,项目已经很难维护和扩展了,为新系统提供一个外观Facade类,来提供实际粗糙或者高度复杂的遗留代码的比较清晰的接口,让新系统与Facade对象交互,Facade类与遗留代码进行复杂的交互工作 ## 9.建造者模式 - 建造者模式,将一个复杂对象的构建和表示分离,使得同样的构建过程可以构建不同的表示 - 建造者模式是当在创建一个复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时适用 ### 示例 产品类: ```java /** * 产品类,由多个零件组成 * * @author wdl */ public class Product { List parts = new ArrayList<>(); public void add(String part) { parts.add(part); } public void show() { System.out.println("\n产品构建----"); for (String part : parts) { System.out.println(part); } } } ``` 抽象的建造者: ```java /** * 抽象的构建者 * 确定产品由两个部件PartA和PartB * @author wdl */ public abstract class Builder { /** * 构建部件A */ public abstract void buildPartA(); /** * 构建部件B */ public abstract void buildPartB(); /** * 得到产品结果 * @return 得到产品构建后的结果 */ public abstract Product getResult(); } ``` 具体的建造者: ```java /** * 具体的构建者1 * @author wdl */ public class ConcreteBuilder1 extends Builder{ private Product product=new Product(); /** * 构建具体的两个部件 */ @Override public void buildPartA() { product.add("部件X"); } @Override public void buildPartB() { product.add("部件Y"); } @Override public Product getResult() { return product; } } ``` 指挥建造过程: ```java /** * 指挥者类 * @author wdl */ public class Director { /** * 指挥构建的过程 * @param builder 构建的产品 */ public void buildProduct(Builder builder){ builder.buildPartA(); builder.buildPartB(); } } ``` 适用: ```java /** * p112 * 建造者模式 * @author wdl */ public class BuilderPattern { public static void main(String[] args) { Director director=new Director(); Builder builder1=new ConcreteBuilder1(); Builder builder2=new ConcreteBuilder2(); //指挥者构建产品 director.buildProduct(builder1); Product product1=builder1.getResult(); product1.show(); director.buildProduct(builder2); Product product2=builder2.getResult(); product2.show(); } } ``` ## 10.观察者模式 - 观察者模式又称为发布-订阅模式(Publish/Subscribe) - 观察者模式定义了一种一对多的依赖关系,让多个观察者对象同事监听某个主题对象。这个主题对象在状态发生变化时,通知所有观察者,使它们能够自动更新自己 ### 示例 ```java /** * 主题即抽象的通知者 * @author wdl */ public abstract class Subject { private List observers=new ArrayList<>(); //增加观察者 public void attach(Observer observer){ observers.add(observer); } //移除观察者 public void detach(Observer observer){ observers.remove(observer); } //通知 public void Notify(){ for (Observer observer:observers){ observer.update(); } } } ``` ```java /** * 抽象的观察者 * * @author wdl */ public abstract class Observer { public abstract void update(); } ``` ```java /** * 具体的通知者 * @author wdl */ public class ConcreteSubject extends Subject{ private String subjectState; public String getSubjectState() { return subjectState; } public void setSubjectState(String subjectState) { this.subjectState = subjectState; } } ``` ```java /** * 具体的观察者 * @author wdl */ public class ConcreteObserver extends Observer { private String name; private String observerState; private ConcreteSubject subject; public ConcreteObserver(String name, ConcreteSubject subject) { this.name = name; this.subject = subject; } @Override public void update() { observerState=subject.getSubjectState(); System.out.println(name+"观察者的状态是"+observerState); } public ConcreteSubject getSubject() { return subject; } public void setSubject(ConcreteSubject subject) { this.subject = subject; } } ``` 使用: ```java /** * 观察者模式 * @author wdl */ public class ObserverPattern { public static void main(String[] args) { ConcreteSubject subject=new ConcreteSubject(); //增加观察者 subject.attach(new ConcreteObserver("X",subject)); subject.attach(new ConcreteObserver("Y",subject)); subject.attach(new ConcreteObserver("Z",subject)); //改变状态 subject.setSubjectState("ABC"); //发起通知 subject.Notify(); } } ``` ### 不足 - 观察者模式不足的地方:将一个系统分割成相互协作的类,需要维护相关对象间的一致性,使得各个类的耦合度较高 - 通知者需要知道观察者的更新方法,存在耦合 ### 适用场景 - 当一个对象的改变需要同时改变其它对象 ### 事件委托 - 通过事件委托可以改良观察者模式 - 事件委托,解决问题:一个委托可以**搭载多个方法**,所有方法被一次唤起。更重要的是委托可以使得委托对象所搭载的方法并**不需要属于同一个类**,这样我们就可以解开本来通知者与抽象观察者的耦合。 - 委托是一种引用方法的类型,一旦为委托分配了方法,委托将于该方法具有完全相同的行为,委托方法的使用可以向其他任何方法一样,具有参数和返回值。委托可以看做是对函数的抽象,是函数的“类”,委托的实例将代表一个具体的函数。 - Java中委托通过类的反射机制实现 Event类: ```java /** * 处理的事件类 * 通过反射获取到执行方法的对象和方法名方法参数 * * @author wdl */ public class Event { /** * 要执行方法的对象 */ private Object object; /** * 要执行方法的方法名 */ private String methodName; /** * 要执行方法的参数 */ private Object[] params; public Object getObject() { return object; } public void setObject(Object object) { this.object = object; } public String getMethodName() { return methodName; } public void setMethodName(String methodName) { this.methodName = methodName; } public Object[] getParams() { return params; } public void setParams(Object[] params) { this.params = params; } /** * 要执行方法的参数类型 */ private Class[] paramTypes; public Class[] getParamTypes() { return paramTypes; } public void setParamTypes(Class[] paramTypes) { this.paramTypes = paramTypes; } /** * 根据参数列表生成参数类型列表 * * @param params 参数列表 */ private void contractParamTypes(Object[] params) { this.paramTypes = new Class[params.length]; //遍历获取每个参数的类型 for (int i = 0; i < params.length; i++) { this.paramTypes[i] = params[i].getClass(); } } /** * 构造函数 */ public Event() { } public Event(Object object, String methodName, Object[] params) { this.object = object; this.methodName = methodName; this.params = params; contractParamTypes(params); } /** * 执行该对象的该方法(这里使用反射) */ public void invoke() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { Method method=object.getClass().getMethod(this.getMethodName(),this.getParamTypes()); if (method==null){ return; } method.invoke(this.getObject(),this.getParams()); } } ``` 事件处理程序: ```java /** * Event的处理类 * @author wdl */ public class EventHandler { private List events; public EventHandler(){ events=new ArrayList<>(); } /** * 添加对象和要执行的方法 * @param object 执行的对象 * @param methodName 执行的方法名 * @param args 方法的参数 */ public void addEvent(Object object,String methodName,Object...args){ events.add(new Event(object,methodName,args)); } /** * 通知所有对象执行方法操作 */ public void notifyX() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { for (Event event:events){ event.invoke(); } } } ``` 通知者抽象: ```java /** * 通知者抽象类 * @author wdl */ public abstract class Notifier { private EventHandler eventHandler=new EventHandler(); public EventHandler getEventHandler() { return eventHandler; } public void setEventHandler(EventHandler eventHandler) { this.eventHandler = eventHandler; } /** * 添加需要报名放哨的同事 * @param object 需要的同事 * @param methodName 同事执行的方法 * @param args 方法参数 */ public abstract void addListener(Object object,String methodName,Object...args); /** * 通知同事 */ public abstract void notifyX(); } ``` 使用: 这里与观察者模式结合 ```java /** * 抽象的通知者 * @author wdl */ public interface ISubject { /** * 通知方法 */ void notifyX(); /** * 获取通知状态 * @return 返回当前通知状态 */ String getSubjectState(); /** * 设置状态 * @param action 设置状态 */ void setSubjectState(String action); } ``` 具体的通知者: ```java /** * 具体的通知者前台秘书 * @author wdl */ public class Secretary extends Notifier implements ISubject{ @Override public void addListener(Object object, String methodName, Object... args) { System.out.println("有新的同事委托尽职尽责的放哨人!"); this.getEventHandler().addEvent(object, methodName, args); } @Override public void notifyX() { try { this.getEventHandler().notifyX(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } private String action; @Override public String getSubjectState() { return action; } @Override public void setSubjectState(String action) { this.action=action; } } ``` ```java /** * 看股票的同事 * @author wdl */ public class StockObserver { private String name; private ISubject sub; public StockObserver(String name,ISubject sub){ this.name=name; this.sub=sub; } /** * 关闭股票继续工作 */ public void closeStockMarket(){ System.out.println(sub.getSubjectState()+","+name+"关闭股票继续工作"); } } ``` 具体调用: ```java /** * 通过事件委托实现观察者模式 * * @author wdl */ public class ObserverPatternByEventCommission { public static void main(String[] args) { //前台秘书 Secretary secretary = new Secretary(); //看股票的同事 StockObserver stockObserver = new StockObserver("同事1", secretary); //看NBA的同事 NBAObserver nbaObserver = new NBAObserver("同事2", secretary); secretary.addListener(stockObserver, "closeStockMarket"); secretary.addListener(nbaObserver, "closeNBA"); secretary.setSubjectState("老板来了"); secretary.notifyX(); } } ``` ## 11.*抽象工厂模式 - 提供一个创建一系列相关或互相依赖对象的接口,而无需指定它们具体的类 ### 好处 - 将产品系列抽象出来,易于交互产品系列,由于具体的工程类例如IFactory factory=new AccessFactory()在一个应用中只需要初始化时出现一次,使得改变应用的具体工厂非常容易,只需要改变具体工厂即可使用不同的产品配置。 - 它让具体的创建实例过程与客户端分离,客户端通过它们的抽象接口操作实例,产品的具体类名也被具体工厂实现分离,不会出现在客户代码中。 ## 12.状态模式 - 面向对象设计其实就是希望做到代码的责任分解 - 状态模式:当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类 - 状态模式主要解决的是当控制一个对象的状态装换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类当中,把复杂的判断逻辑简化 - 其实就是将特定状态相关行为放到一个对象之中,由于所有与状态相关的代码都存在于某个具体的状态中,所以通过定义新的子类就可以容易地增加新的状态和装换,这样就可以消除庞大的判断分支语句 ### 示例 工作类修改前: ```java /** * 工作类 * * @author wdl */ public class Work { /** * 工作的时间点 */ private int hour; /** * 工作是否已经完成 */ private boolean finishWork; public void setHour(int hour) { this.hour = hour; } public void setFinishWork(boolean finishWork) { this.finishWork = finishWork; } /** * 写代码 * 这边逻辑判断比较的复杂,同时破坏了开闭原则 */ public void writeProgram(){ if (hour<12){ System.out.println("当前时间"+hour+",上午工作,精神百倍"); }else if (hour<13){ System.out.println("当前时间"+hour+",午饭时间,困"); }else if (hour<17){ System.out.println("当前时间"+hour+",下午状态不错,继续努力"); }else { if (finishWork){ System.out.println("当前时间"+hour+",下班回家"); }else { if (hour<21){ System.out.println("当前时间"+hour+",加班工作"); }else { System.out.println("当前时间"+hour+",工作没做完,但是必须回去休息了"); } } } } } ``` 状态模式进行修改: ```java /** * 状态的抽象接口 * @author wdl */ public interface IState { void writeProgram(Work work); } ``` 具体的状态: ```java /** * 上午的工作状态 * @author wdl */ public class ForenoonState implements IState { @Override public void writeProgram(Work work) { if (work.getHour()<12){ System.out.println("当前时间"+work.getHour()+",上午工作,精神百倍"); }else { //不满足条件则切换到下一个中午的状态 work.setState(new NoonSate()); work.writeProgram(); } } } ``` ```java /** * 中午工作状态 * @author wdl */ public class NoonSate implements IState { @Override public void writeProgram(Work work) { if (work.getHour()<13){ System.out.println("当前时间"+work.getHour()+",午饭时间,困"); }else { //不满足条件就切换到下一个下午的状态 work.setState(new AfternoonState()); work.writeProgram(); } } } ``` ```java /** * 下午的工作状态 * @author wdl */ public class AfternoonState implements IState { @Override public void writeProgram(Work work) { if (work.getHour()<17){ System.out.println("当前时间"+work.getHour()+",下午状态不错,继续努力"); }else { //不满足条件就切换到下一个晚上的工作状态 work.setState(new EveningState()); work.writeProgram(); } } } ``` ```java /** * 夜间工作状态 * @author wdl */ public class EveningState implements IState { @Override public void writeProgram(Work work) { if (work.isFinishWork()){ System.out.println("当前时间"+work.getHour()+",下班回家"); }else { //切换到加班的状态 work.setState(new SleepingState()); work.writeProgram(); } } } ``` ```java /** * 休息的状态 * @author wdl */ public class SleepingState implements IState { @Override public void writeProgram(Work work) { if (work.getHour()<21){ System.out.println("当前时间"+work.getHour()+",加班工作"); }else { System.out.println("当前时间"+work.getHour()+",工作没做完,但是必须回去休息了"); } } } ``` 工作类用于控制状态的切换 ```java /** * 工作类 * * @author wdl */ public class Work { /** * 工作的时间点 */ private int hour; /** * 工作是否已经完成 */ private boolean finishWork; /** * 工作的状态 * */ private IState state; /** * 初始化工作状态 * @param state 初始化状态 */ public Work(IState state){ this.state=state; } public void setHour(int hour) { this.hour = hour; } public int getHour() { return hour; } public boolean isFinishWork() { return finishWork; } public void setFinishWork(boolean finishWork) { this.finishWork = finishWork; } public IState getState() { return state; } public void setState(IState state) { this.state = state; } /** * 写代码 */ public void writeProgram(){ state.writeProgram(this); } } ``` 使用 ```java /** * 加班的示例 * @author wdl */ public class OverTime { public static void main(String[] args) { //紧急任务,初始化工作状态 Work emergencyProjects=new Work(new ForenoonState()); emergencyProjects.setHour(9); emergencyProjects.writeProgram(); emergencyProjects.setHour(10); emergencyProjects.writeProgram(); emergencyProjects.setHour(12); emergencyProjects.writeProgram(); emergencyProjects.setHour(13); emergencyProjects.writeProgram(); emergencyProjects.setHour(14); emergencyProjects.writeProgram(); emergencyProjects.setHour(17); emergencyProjects.setFinishWork(false); // emergencyProjects.setFinishWork(true); emergencyProjects.writeProgram(); emergencyProjects.setHour(22); emergencyProjects.writeProgram(); } } ``` ### 好处 - 状态模式将与特定状态相关的行为局部化,并且将不同状态的行为分割开来 ## 13.适配器模式 - 将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能工作的类可以一起工作了 - 在软件开发中,系统的数据和行为都是正确的,但是接口不符时,我们应该考虑使用适配器,目的是使控制范围外的一个原有对象与某个接口匹配。 - 适配器模式有两种类型:类适配器模式和对象适配器模式 - 类适配器模式:通过多重继承对一个接口和另一个接口进行适配(C++支持多重继承,Java、C#等语言不支持) ### 示例 首先是大家共同的接口 ```java /** * ITarget(这是用户所期待的接口,目标可以使抽象或具体的类,也可以是接口) * @author wdl */ public interface ITarget { /** * 目标的普通请求 */ void targetRequest(); } ``` 有一个特殊的接口请求是需要适配的 ```java /** * 需要适配的特殊请求 * @author wdl */ public class Adaptee { public void specificRequest(){ System.out.println("特殊请求"); } } ``` 通过适配器,持有特殊接口的对象,将目标的请求转换为正确的需要的特殊接口的请求 ```java /** * 适配器,通过内部包装一个Adaptee对象,将目标请求转换为实际的特殊请求 * @author wdl */ public class Adapter implements ITarget{ /** * 建立一个私有的Adaptee对象 */ private Adaptee adaptee=new Adaptee(); @Override public void targetRequest() { //把表面上的调用的targetRequest方法变成实际调用的specificRequest方法 adaptee.specificRequest(); } } ``` 通过适配器就可以调用到需要的接口方法 ```java /** * p171 * 适配器模式 * @author wdl */ public class AdapterPattern { public static void main(String[] args) { //对于客户来说调用targetRequest ITarget target=new Adapter(); target.targetRequest(); } } ``` ### 适用情况 - 适配器模式主要用于希望复用一些现存的类,但是接口又和复用的类不一致的情况。 ## 14.备忘录模式 - 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到原先保存的状态 ### 示例 备份类:用于保存备份信息 ```java /** * 备忘录类 * @author wdl */ public class Memento { private String state; /** * 通过构造函数,将相关数据传入 * @param state 相关数据 */ public Memento(String state){ this.state=state; } /** * 获取相关数据 * @return 相关备份数据返回 */ public String getState() { return state; } } ``` 发起者类:备份恢复备份的发起者 ```java /** * 发起者类 * * @author wdl */ public class Originator { private String state; public String getState() { return state; } public void setState(String state) { this.state = state; } /** * 创建一个备份 * * @return 返回备份 */ public Memento createMemento() { return new Memento(state); } /** * 恢复备份 * @param memento 备份数据对象 */ public void getStateFromMemento(Memento memento) { state = memento.getState(); } public void show(){ System.out.println(state); } } ``` 管理类:存储备份和获取备份,是备份的管理 ```java /** * 管理类 * * @author wdl */ public class Caretaker { private Memento memento; //设置和获取备份的方法 public Memento getMemento() { return memento; } public void saveMemento(Memento memento) { this.memento = memento; } } ``` 使用 ```java /** * p180 * 备忘录模式 * @author wdl */ public class MementoPattern { public static void main(String[] args) { //初始化发起者并设置初始化状态为On Originator originator=new Originator(); originator.setState("On"); originator.show(); //创建管理者 Caretaker caretaker=new Caretaker(); //管理者保存备份 caretaker.saveMemento(originator.createMemento()); //设置新的状态 originator.setState("Off"); originator.show(); //恢复备份 originator.getStateFromMemento(caretaker.getMemento()); originator.show(); } } ``` ## 15.组合模式 - 将对象组合成树型结构以表示“部分-整体”的层次结构。组合模式使得用户对于单个对象和组合对象的使用具有一致性。 ### 示例 Component为组合中的对象声明接口,实现所有类的公共接口的默认行为 ```java /** * 组合对象的声明接口,在适当的情况下,实现所有类的公共接口的默认行为 * @author wdl */ public abstract class BaseComponent { protected String name; public BaseComponent(String name) { this.name = name; } /** * 增加功能 * @param component 组合对象 */ public abstract void add(BaseComponent component); /** * 移除功能 * @param component 组合对象 */ public abstract void remove(BaseComponent component); /** * 显示功能 * @param depth 深度 */ public abstract void display(int depth); } ``` Leaf在组合中表示叶子节点对象 ```java /** * “树叶”没有再增加或减少分支的需要,所以add与remove方法没有意义, * 但是可以叶子节点和非叶子节点在抽象层的区别,使得它们具有完全一致的接口 * * @author wdl */ public class Leaf extends BaseComponent { public Leaf(String name) { super(name); } @Override public void add(BaseComponent component) { System.out.println("叶子节点不能再增加子节点"); } @Override public void remove(BaseComponent component) { System.out.println("叶子节点没有子节点可以移除"); } @Override public void display(int depth) { for (int i = 0; i < depth; i++) { System.out.print("-"); } System.out.println(name); } } ``` 非叶子节点用Composite表示 ```java /** * 非叶子节点 * * @author wdl */ public class Composite extends BaseComponent { public Composite(String name) { super(name); } private ArrayList components = new ArrayList<>(); @Override public void add(BaseComponent component) { components.add(component); } @Override public void remove(BaseComponent component) { components.remove(component); } @Override public void display(int depth) { for (int i = 0; i < depth; i++) { System.out.print("-"); } System.out.println(name); for (BaseComponent component : components) { component.display(depth + 2); } } } ``` 使用 ```java /** * p189 * 组合模式 * @author wdl */ public class CompositePattern { public static void main(String[] args) { Composite root=new Composite("root"); root.add(new Leaf("Leaf A")); root.add(new Leaf("Leaf B")); Composite composite1=new Composite("Composite1"); composite1.add(new Leaf("Leaf1 A")); composite1.add(new Leaf("Leaf1 B")); root.add(composite1); Composite composite2=new Composite("Composite2"); composite2.add(new Leaf("Leaf2 A")); composite2.add(new Leaf("Leaf2 B")); composite1.add(composite2); root.add(new Leaf("Leaf C")); Leaf leafD=new Leaf("Leaf D"); root.add(leafD); root.display(1); } } ``` 结果 ```java -root ---Leaf A ---Leaf B ---Composite1 -----Leaf1 A -----Leaf1 B -----Composite2 -------Leaf2 A -------Leaf2 B ---Leaf C ---Leaf D ``` ### 透明方式与安全方式 - 以上的方式为透明方式,也就是说在Component中声明所有用来管理子类对象的方法,其中包括Add、Remove等。这样实现Component接口的所有子类都具备Add、Remove。这样做的好处就是叶子节点和非叶子节点对于外界没有区别,它们具备完全一致的行为接口,但是也意味着叶子节点实现了没有意义的方法 - 安全方式:在Component接口中不去声明Add、Remove方法,那么子类的Leaf不必实现两个无意义的方法,而在Composite接口中去实现管理子类使用的方法如Add、Remove,这样叶子类与非叶子类实现的接口就是不同的,客户端调用需要做相应的判断,透明度减低但是判断带来了不便。 ### 使用场景 - 需求中体现出部分与整体的结构时 - 希望用户忽略组合对象和单个对象的不同,统一的使用组合结构中的所有对象时 ## 16.迭代器模式 - 迭代器模式,提供一种顺序访问一个聚合对象中各个元素,而不暴露聚合对象的内部表示 ### 适用场景 - 你需要访问一个聚合对象,并且不管对象内部是什么都需要遍历 - 对聚合对象有多种方式遍历(比如从前往后,从后往前等) ### 示例 为遍历不同的聚合结构提供统一的接口 ```java /** * 为不同的聚合对象提供统一的如开始、下一个、最后一个等统一的接口 * @author wdl */ public interface IIterator { /** * 获取第一个对象 * @return 第一个对象 */ Object first(); /** * 获取下一个对象 * @return 下一个对象 */ Object next(); /** * 是否已经到达聚合对象的结尾 * @return 是否结尾 */ boolean isDone(); /** * 获取当前对象 * @return 当前对象 */ Object currentItem(); } ``` Aggregate聚合对象接口 ```java /** * 聚合对象的接口 * @author wdl */ public interface IAggregate { /** * 创建迭代器 * @return 迭代器 */ IIterator createIterator(); } ``` 具体的迭代器 ```java /** * 具体的迭代器 * * @author wdl */ public class ConcreteIterator implements IIterator { /** * 定义了一个具体的聚合对象 */ private ConcreteAggregate aggregate; private int current = 0; public ConcreteIterator(ConcreteAggregate aggregate) { this.aggregate = aggregate; } @Override public Object first() { return aggregate.getByIndex(0); } @Override public Object next() { Object ret = null; current++; if (current < aggregate.getCount()) { ret = aggregate.getByIndex(current); } return ret; } @Override public boolean isDone() { return current >= aggregate.getCount(); } @Override public Object currentItem() { return aggregate.getByIndex(current); } } ``` 具体的聚合对象 ```java /** * 具体的聚合对象 * * @author wdl */ public class ConcreteAggregate implements IAggregate { private List items = new ArrayList<>(); @Override public IIterator createIterator() { return new ConcreteIterator(this); } public int getCount() { return items.size(); } public void add(Object value) { items.add(value); } public Object getByIndex(int index) { return items.get(index); } } ``` 使用 ```java /** * p200 * 迭代器模式 * @author wdl */ public class IteratorPattern { public static void main(String[] args) { ConcreteAggregate concreteAggregate=new ConcreteAggregate(); concreteAggregate.add("大鸟"); concreteAggregate.add("小菜"); concreteAggregate.add("行李"); concreteAggregate.add("老外"); concreteAggregate.add("公交内部员工"); concreteAggregate.add("小偷"); IIterator iterator=new ConcreteIterator(concreteAggregate); while (!iterator.isDone()){ System.out.println(iterator.currentItem()+",请买车票"); iterator.next(); } } } ``` 结果 ```java 大鸟,请买车票 小菜,请买车票 行李,请买车票 老外,请买车票 公交内部员工,请买车票 小偷,请买车票 ``` ## 17.单例模式 [单例模式—菜鸟教程]() - 单例模式,保证一个类仅有一个实例,并提供一个访问它的全局访问点 - 通常我们可以使用全局变量来使得一个对象被访问,但是不能防止实例化多个对象。所以最好的方法就是,让类本身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法 ### 1、懒汉式,线程不安全 **是否 Lazy 初始化:**是 **是否多线程安全:**否 **实现难度:**易 **描述:**这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。 这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。 ```java public class Singleton1 { private static Singleton1 instance; /** * 构造函数私有,不允许外界使用new来创建新的实例 */ private Singleton1(){} /** * 获取本类实例的全局访问点 * @return 返回类的唯一实例 */ public static Singleton1 getInstance(){ if (instance==null){ instance=new Singleton1(); } return instance; } } ``` ### 2、懒汉式,线程安全 **是否 Lazy 初始化:**是 **是否多线程安全:**是 **实现难度:**易 **描述:**这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。 优点:第一次调用才初始化,避免内存浪费。 缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。 getInstance() 的性能对应用程序不是很关键(该方法使用不太频繁)。 ```java public class Singleton2 { private static Singleton2 instance; private Singleton2(){} //加锁 private static synchronized Singleton2 getInstance(){ if (instance==null){ instance=new Singleton2(); } return instance; } } ``` ### 3、饿汉式 **是否 Lazy 初始化:**否 **是否多线程安全:**是 **实现难度:**易 **描述:**这种方式比较常用,但容易产生垃圾对象。 优点:没有加锁,执行效率会提高。 缺点:类加载时就初始化,浪费内存。 它基于 classloader 机制避免了多线程的同步问题,不过,**instance 在类装载时就实例化**,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。 ```java public class Singleton3 { /** * 在类装载的时候就直接实例化一个对象 */ private static Singleton3 instance=new Singleton3(); private Singleton3(){} public static Singleton3 getInstance(){ return instance; } } ``` ### 4、双检锁/双重校验锁(DCL,即 double-checked locking) **JDK 版本:**JDK1.5 起 **是否 Lazy 初始化:**是 **是否多线程安全:**是 **实现难度:**较复杂 **描述:**这种方式采用双锁机制,安全且在多线程情况下能保持高性能。 getInstance() 的性能对应用程序很关键。 ```java public class Singleton4 { /** * volatile:保证可见性 */ private static volatile Singleton4 instance; private Singleton4(){} public static Singleton4 getInstance(){ //第一重判断instance是否存在 if (instance==null){ synchronized (Singleton4.class){ /** * 第二重判断instance是否存在是为了防止多线程同时调用getInstance()方法时 * 它们都能够通过第一层instance==null判断, * 由于前面的锁机制,因此仅有一个线程可以进入,其它线程等待 * 当进入线程完成实例创建,如果没有第二重判断,其它线程也可以创建新的实例 */ if (instance==null){ instance=new Singleton4(); } } } return instance; } } ``` ### 5、登记式/静态内部类 **是否 Lazy 初始化:**是 **是否多线程安全:**是 **实现难度:**一般 **描述:**这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。 这种方式同样利用了 classloader 机制来保证初始化 instance 时只有一个线程,它跟第 3 种方式不同的是:第 3 种方式只要 Singleton 类被装载了,那么 instance 就会被实例化(没有达到 lazy loading 效果),而这种方式是 Singleton 类被装载了,instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。想象一下,如果实例化 instance 很消耗资源,所以想让它延迟加载,另外一方面,又不希望在 Singleton 类加载时就实例化,因为不能确保 Singleton 类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化 instance 显然是不合适的。这个时候,这种方式相比第 3 种方式就显得很合理。 ```java public class Singleton5 { private static class SingletonHolder { private static final Singleton5 INSTANCE = new Singleton5(); } private Singleton5() { } public static final Singleton5 getInstance() { return SingletonHolder.INSTANCE; } } ``` ### 6、枚举 **JDK 版本:**JDK1.5 起 **是否 Lazy 初始化:**否 **是否多线程安全:**是 **实现难度:**易 **描述:**这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。 这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。 不能通过 reflection attack 来调用私有构造方法。 ```java public enum Singleton6 { /** * 实例化对象 */ INSTANCE; /** * 方法 */ public void whateverMethod() { } public void showMessage(){ System.out.println("枚举的单例模式"); } } ``` 使用: ```java /** * p209 * 单例模式 * @author wdl */ public class SingletonPattern { public static void main(String[] args) { Singleton1 singleton1=Singleton1.getInstance(); singleton1.showMessage(); Singleton2 singleton2=Singleton2.getInstance(); singleton2.showMessage(); Singleton3 singleton3=Singleton3.getInstance(); singleton3.showMessage(); Singleton4 singleton4=Singleton4.getInstance(); singleton4.showMessage(); Singleton5 singleton5=Singleton5.getInstance(); singleton5.showMessage(); Singleton6.INSTANCE.showMessage(); } } ``` ### 懒汉与饿汉 - 饿汉:类以加载就实例化对象,面临提前占用系统资源的问题 - 懒汉:调用时实例化对象,面临多线程访问的性能问题 ## 18.桥接模式 继承带来的一些不便 - 对象的继承关系是在编译时就定义好了,所以无法在运行时去改变从父类继承的实现 - 子类的实现与父类有非常紧密的依赖关系,以至于父类实现中的任何改变必然会导致子类发生变化 - 当需要复用子类时,如果继承下来的实现不合适解决新的问题,则父类必须重写或被其他更合适的类替换 - 这种依赖关系限制了灵活性并最终限制了复用性 ### 合成/聚合复用原则 - 聚合表示一种弱的“拥有关系”,提醒对象A可以包含对象B,但是对象B不是对象A - 合成表示一种强的“拥有关系”,提醒了严格的部分与整体的生命周期一样 优先使用对象的合成/聚合有助于保持每个类被封装,并集中在单个任务上。这样类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物 ### 桥接模式 - 桥接模式,将抽象部分与它的实现部分分离,使它们都能独立的变化 - 抽象与实现分离,实现指的是抽象类和它的派生类用来实现自己的对象,例如手机可以按照品牌分类也可以按照功能分类 Implementor类 ```java public abstract class BaseImplementor { public abstract void operation(); } ``` ConcreteImplementorA和ConcreteImplementorB等具体的派生类(具体的实现) ```java public class ConcreteImplementorA extends BaseImplementor { @Override public void operation() { System.out.println("具体实现类A的方法执行"); } } public class ConcreteImplementorB extends BaseImplementor { @Override public void operation() { System.out.println("具体实现类B的方法执行"); } } ``` 抽象类 ```java public abstract class Abstraction { protected BaseImplementor implementor; public void setImplementor(BaseImplementor implementor){ this.implementor=implementor; } public abstract void operation(); } ``` 被提炼的抽象 ```java public class RefinedAbstraction extends Abstraction { @Override public void operation() { implementor.operation(); } } ``` 使用 ```java public class BridgePattern { public static void main(String[] args) { Abstraction ab=new RefinedAbstraction(); ab.setImplementor(new ConcreteImplementorA()); ab.operation(); ab.setImplementor(new ConcreteImplementorB()); ab.operation(); } } ``` ### 使用场景 1. 如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。 2. 对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。 3. 一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。 ## 19.命令模式 - "行为请求者"与"行为实现者"的紧耦合 - 命令模式,将一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化;对于请求排队或则记录请求日志,以及支持可撤销的操作。 ### 示例 Command类,声明执行操作的接口 ```java /** * 抽象的Command类,用来声明执行操作的接口 * @author wdl */ public abstract class BaseCommand { protected Receiver receiver; /** * 设置一个命令接收者 * @param receiver 接收者 */ public BaseCommand(Receiver receiver) { this.receiver = receiver; } /** * 执行请求 */ abstract public void execute(); } ``` ConcreteCommand类,将一个接收者绑定于一个动作,调用接收者的具体方法实现execute ```java /** * 具体的命令ConcreteCommand类,将一个接收者绑定于一个动作,调用接收者的具体方法实现execute * @author wdl */ public class ConcreteCommand extends BaseCommand { /** * 设置一个命令接收者 * * @param receiver 接收者 */ public ConcreteCommand(Receiver receiver) { super(receiver); } @Override public void execute() { receiver.action(); } } ``` Invoker类,要求该命令执行这个请求 ```java /** * Invoker类,要求该命令执行这个请求 * @author wdl */ public class Invoker { private BaseCommand command; /** * 设置具体的命令 * @param command 具体命令 */ public void setCommand(BaseCommand command){ this.command=command; } public void executeCommand(){ command.execute(); } } ``` Receiver类,知道如何实施与执行一个与请求相关的操作,任何类都可能作为一个接收者 ```java /** * Receiver类,知道如何实施与执行一个与请求相关的操作,任何类都可能作为一个接收者 * @author wdl */ public class Receiver { public void action(){ System.out.println("执行请求"); } } ``` 客户端 创建一个具体命令对象设定它的接收者 ```java /** * p234 * 命令模式 * @author wdl */ public class CommandPattern { public static void main(String[] args) { Receiver receiver=new Receiver(); BaseCommand command=new ConcreteCommand(receiver); Invoker invoker=new Invoker(); invoker.setCommand(command); invoker.executeCommand(); } } ``` ### 优点 - 它容易设计一个命令队列 - 在需要的情况下,可以容易的把命令记入日志 - 允许接收请求的一方决定是否要否决请求 - 容易对请求实现撤销和重做 - 由于加入新的命令类不影响其他的类,因此增加新的具体的类很容易实现 - 把请求的一个操作的对象与知道怎么执行一个操作的对象分割开 ## 20.责任链模式 - 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止 ### 示例 Handler类,定义处理请求的接口 ```java /** * Handler类定义一个处理请求的接口 * @author wdl */ public abstract class BaseHandler { protected BaseHandler successor; /** * 设置继任者 * @param successor 继任者 */ public void setSuccessor(BaseHandler successor){ this.successor=successor; } /** * 处理请求的抽象方法 * @param request 请求 */ public abstract void handleRequest(int request); } ``` 具体的请求类 ```java /** * 具体处理请求的类,处理0~10 * @author wdl */ public class ConcreteHandler1 extends BaseHandler { @Override public void handleRequest(int request) { if (request>=0&&request<10){ System.out.println(this.getClass().getName()+"处理请求"+request); } //传递给下一个对象处理 else if (successor!=null){ successor.handleRequest(request); } } } /** * 具体处理请求的类,处理10~20 * @author wdl */ public class ConcreteHandler2 extends BaseHandler { @Override public void handleRequest(int request) { if (request>=10&&request<20){ System.out.println(this.getClass().getName()+"处理请求"+request); } //传递给下一个对象处理 else if (successor!=null){ successor.handleRequest(request); } } } /** * 具体处理请求的类,处理20~30 * @author wdl */ public class ConcreteHandler3 extends BaseHandler { @Override public void handleRequest(int request) { if (request>=20&&request<30){ System.out.println(this.getClass().getName()+"处理请求"+request); } //传递给下一个对象处理 else if (successor!=null){ successor.handleRequest(request); } } } ``` 客户端 ```java /** * p254 * 责任链模式 * * @author wdl */ public class ChainOfResponsibilityPattern { public static void main(String[] args) { BaseHandler handler1 = new ConcreteHandler1(); BaseHandler handler2 = new ConcreteHandler2(); BaseHandler handler3 = new ConcreteHandler3(); handler1.setSuccessor(handler2); handler2.setSuccessor(handler3); int[] requests = {2, 5, 14, 22, 28, 3, 7, 27, 20}; for (int request : requests) { handler1.handleRequest(request); } } } ``` ### 优点 - 当用户提交一个请求时,请求是沿着链传递直至有一个请求处理对象负责处理它 - 接收者和发送者都没有对方的明确信息,且链中的对象自己并不知道链的结构。简化对象的相互连接,它们仅需要保持一个指向后继者的引用,而不需要保持它所有的候选接收者的引用 - 可以随时增加或修改处理一个请求的结果。增强了给对象指派职责的灵活性 - 需要注意的是如果一个请求到链末端都得不到处理,或者因为没有正确配置得不到处理的情况 ## 21.中介者模式 - 尽管将一个系统分割成许多对象通常可以增加其可复用性,但是对象间相互连接的激增又会降低对象的复用性,因为大量的连接使得一个对象不可能在没有其它对象的支持下工作,系统表现为一个不可分割的整体,所以,对系统的行为进行较大的改动就很困难 - 中介者模式,用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立改变它们之间的交互 ### 示例 中介者接口/抽象对象 ```java /** * Mediator类(抽象)的中介者接口 * @author wdl */ public interface IMediator { /** * 发送消息方法 * @param message 发送的消息 * @param colleague 同事对象 */ void send(String message,BaseColleague colleague); } ``` 抽象的同事类 ```java /** * BaseColleague,抽象的同事类 * @author wdl */ public class BaseColleague { protected IMediator mediator; /** * 得到中介者对象 * @param mediator 中介者 */ public BaseColleague(IMediator mediator){ this.mediator=mediator; } } ``` 具体同事类,需要认识中介者对象,方便通过中介者发消息 ```java /** * 具体的同事类 * @author wdl */ public class ConcreteColleague1 extends BaseColleague { /** * 得到中介者对象 * * @param mediator 中介者 */ public ConcreteColleague1(IMediator mediator) { super(mediator); } /** * 发送消息 * @param message 消息 */ public void sendMessage(String message){ //通过中介者发送消息 mediator.send(message,this); } /** * 获得消息通知 * @param message 消息 */ public void notifyX(String message){ System.out.println("同事1得到消息:"+message); } } ``` 具体的中介类,需要认识所有具体的同事,方便传递消息 ```java /** * 具体的中介者类 * * @author wdl */ public class ConcreteMediator implements IMediator { /** * 需要了解所有的具体的同事对象 */ private ConcreteColleague1 colleague1; private ConcreteColleague2 colleague2; public void setColleague1(ConcreteColleague1 colleague1) { this.colleague1 = colleague1; } public void setColleague2(ConcreteColleague2 colleague2) { this.colleague2 = colleague2; } /** * 重写发送信息方法,根据对象作出选择判断,通知对象 * @param message 发送的消息 * @param colleague 同事对象 */ @Override public void send(String message, BaseColleague colleague) { if (colleague == colleague1) { colleague2.notifyX(message); } else { colleague1.notifyX(message); } } } ``` 客户端 ```java /** * p257 * 中介者模式 * @author wdl */ public class MediatorPattern { public static void main(String[] args) { //中介者 ConcreteMediator mediator=new ConcreteMediator(); //两个具体的同事类认识中介者 ConcreteColleague1 colleague1=new ConcreteColleague1(mediator); ConcreteColleague2 colleague2=new ConcreteColleague2(mediator); //中介者认识两个具体的同事 mediator.setColleague1(colleague1); mediator.setColleague2(colleague2); //具体同事通过中介者发送信息 colleague1.sendMessage("吃饭了吗?"); colleague2.sendMessage("没有,你打算请客是吗?"); } } ``` ### 优缺点 - 中介者模式很容易在系统中应用,也很容易误用。因此在系统中出现多对多的复杂交互对象群,先反思系统在设计上是否合理 - Mediator的出现减少了各个Colleague的耦合,使得可以独立的改变和复用各个Colleague和Mediator - 由于把对象的协作进行了抽象,将中介作为一个独立的概念并将其封装在一个对象中,这样关注的对象就从对象本身的行为转移到它们之间的交互上来,也是就站在一个更加宏观的角度去看待系统 - 由于ConcreteMediator控制集中化,于是就把交互的复杂性变成了中介者的复杂性,这就使得中介者变得比任何一个ConcreteColleague都复杂,同时中介者出问题也会导致整个系统出问题 ### 使用场景 - 应用于一组以定义良好但是复杂的方式进行通信的场合 - 想定义一个分布在多个类中的行为但是不行生成太多的子类的场合 ## 22.享元模式 - 享元模式,运用共享技术有效地支持大量细粒度的对象 ### 示例 Flyweight它是所有享元类的接口(或者超类),通过这个接口,Flyweight可以接收并作用于外部状态 ```java /** * Flyweight,它是所有享元类的接口(或者超类),通过这个接口,Flyweight可以接收并作用于外部状态 * @author wdl */ public interface IFlyweight { /** * 操作 * @param extrinsicState 外部状态 */ void operation(int extrinsicState); } ``` ConcreteFlyweight实现了Flyweight接口,并为内部状态增加空间 ```java /** * ConcreteFlyweight实现了Flyweight接口,并为内部状态增加空间 * @author wdl */ public class ConcreteFlyweight implements IFlyweight { @Override public void operation(int extrinsicState) { System.out.println(extrinsicState+"具体的Flyweight"); } } ``` UnsharedConcreteFlyweight,不共享的Flyweight类,不强制共享 ```java /** * 不共享的Flyweight类,不强制共享 * @author wdl */ public class UnsharedConcreteFlyweight implements IFlyweight { @Override public void operation(int extrinsicState) { System.out.println(extrinsicState+"不共享的Flyweight"); } } ``` FlyweightFactory是一个享元工厂,创建并管理享元对象 ``` /** * 是一个享元工厂,创建并管理享元对象 * 用来确保合理地共享Flyweight,当用户请求一个Flyweight时,FlyweightFactory对象提供一个已经创建的实例或者创建一个 * @author wdl */ public class FlyweightFactory { private Hashtable flyweights=new Hashtable<>(); /** * 初始化工厂时生成三个实例 */ public FlyweightFactory(){ flyweights.put("X",new ConcreteFlyweight()); flyweights.put("Y",new ConcreteFlyweight()); flyweights.put("Z",new ConcreteFlyweight()); } public IFlyweight getFlyweight(String key){ return flyweights.get(key); } } ``` 客户端 ```java /** * p267 * 享元模式 * @author wdl */ public class FlyweightPattern { public static void main(String[] args) { //代码的外部状态 int extrinsicState=22; FlyweightFactory flyweightFactory=new FlyweightFactory(); IFlyweight flyweightX=flyweightFactory.getFlyweight("X"); flyweightX.operation(--extrinsicState); IFlyweight flyweightY=flyweightFactory.getFlyweight("Y"); flyweightY.operation(--extrinsicState); IFlyweight flyweightZ=flyweightFactory.getFlyweight("Z"); flyweightZ.operation(--extrinsicState); IFlyweight unsharedFlyweight=new UnsharedConcreteFlyweight(); unsharedFlyweight.operation(--extrinsicState); } } ``` ## 23.解释器模式 - 解释器模式,给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子 - 如果一种特定类型的问题发生的频率足够高,那么就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题 ### 示例 BaseExpression抽象表达式,声明一个抽象的解释操作,由抽象语法树中的所有节点共享 ```java /** * 抽象表达式,声明一个抽象的解释操作,由抽象语法树中的所有节点共享 * @author wdl */ public abstract class BaseExpression { /** * 解释操作 * @param context 全局信息 */ public abstract void interpret(Context context); } ``` Context包含解释器外的全局信息 ```java /** * 包含解释器外的全局信息 * @author wdl */ public class Context { private String input; public String getInput() { return input; } public void setInput(String input) { this.input = input; } public String getOutput() { return output; } public void setOutput(String output) { this.output = output; } private String output; } ``` 具体的解释器 ```java /** * 终结符表达式,实现与文法中的终结符相关联的解释操作 * @author wdl */ public class TerminalExpression extends BaseExpression { @Override public void interpret(Context context) { System.out.println("终端解释器"); } } ``` ```java /** * 非终结符表达式,实现与文法中的终结符相关联的解释操作 * @author wdl */ public class NonTerminalExpression extends BaseExpression { @Override public void interpret(Context context) { System.out.println("非终端解释器"); } } ``` 客户端 ```java /** * p279 * 解释器模式 * @author wdl */ public class InterpreterPattern { public static void main(String[] args) { Context context=new Context(); List expressionList=new ArrayList<>(); expressionList.add(new TerminalExpression()); expressionList.add(new NonTerminalExpression()); expressionList.add(new TerminalExpression()); expressionList.add(new TerminalExpression()); for (BaseExpression expression: expressionList) { expression.interpret(context); } } } ``` ### 优缺点 - 有一个语言需要解释执行,并且你可以将该语言中的句子表示为一个抽象的语法树时,可以使用解释器模式 - 解释器模式,可以很容易的改变和扩展语法,因为该模式使用类来表示文法规则,你可以使用继承来改变和扩展该文法。也比较容易实现文法,因为定义在语法树中的各个节点的类的实现大体类似,这些类都易于编写 - 解释器模式为文法中的每一条规则至少定义了一个类,因此包含许多规则的文法可能难以管理和维护。建议文法非常复杂时使用其他技术如语法分析或编译器生成器来处理 ## 24.访问者模式 - 访问者模式表示作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作 - 访问者模式适用于数据结构相对稳定的系统,把数据作用于数据结构上的操作之间的耦合解脱开,使得操作集合可以相对自由的演化 ### 示例 ```java /** * 为ConcreteElement的每一个类声明一个Visit操作 * * @author wdl */ public interface IVisitor { /** * ConcreteElementA对象的visit操作 * * @param concreteElementA ConcreteElementA对象 */ void visitConcreteElementA(ConcreteElementA concreteElementA); /** * ConcreteElementB对象的visit操作 * * @param concreteElementB ConcreteElementB对象 */ void visitConcreteElementB(ConcreteElementB concreteElementB); } ``` ```java /** * 具体的Visitor类 * @author wdl */ public class ConcreteVisitor1 implements IVisitor{ @Override public void visitConcreteElementA(ConcreteElementA concreteElementA) { System.out.println(concreteElementA.getClass().getName()+"被"+this.getClass().getName()+"访问"); } @Override public void visitConcreteElementB(ConcreteElementB concreteElementB) { System.out.println(concreteElementB.getClass().getName()+"被"+this.getClass().getName()+"访问"); } } ``` ```java /** * 具体的Visitor类 * @author wdl */ public class ConcreteVisitor2 implements IVisitor{ @Override public void visitConcreteElementA(ConcreteElementA concreteElementA) { System.out.println(concreteElementA.getClass().getName()+"被"+this.getClass().getName()+"访问"); } @Override public void visitConcreteElementB(ConcreteElementB concreteElementB) { System.out.println(concreteElementB.getClass().getName()+"被"+this.getClass().getName()+"访问"); } } ``` ```java /** * 定义一个接收访问者的操作 * * @author wdl */ public interface IElement { /** * 接收访问对象 * @param visitor 访问对象 */ void accept(IVisitor visitor); } ``` ```java /** * 具体的被访问对象 * @author wdl */ public class ConcreteElementA implements IElement{ /** * 利用双分派技术实现处理与数据结构的分离 * @param visitor 访问对象 */ @Override public void accept(IVisitor visitor) { visitor.visitConcreteElementA(this); } public void operationA(){} } /** * 具体的被访问对象 * * @author wdl */ public class ConcreteElementB implements IElement { /** * 利用双分派技术实现处理与数据结构的分离 * @param visitor 访问对象 */ @Override public void accept(IVisitor visitor) { visitor.visitConcreteElementB(this); } public void operationB(){} } ``` ```java /** * 枚举元素,提供一个高层的接口以运行访问者访问它的元素 * * @author wdl */ public class ObjectStructure { private List elements = new ArrayList<>(); public void attach(IElement element) { elements.add(element); } public void detach(IElement element) { elements.remove(element); } public void accept(IVisitor visitor) { for (IElement e : elements) { e.accept(visitor); } } } ``` ```java /** * p291 * 访问者模式 * * @author wdl */ public class VisitorPattern { public static void main(String[] args) { ObjectStructure objectStructure = new ObjectStructure(); objectStructure.attach(new ConcreteElementA()); objectStructure.attach(new ConcreteElementB()); ConcreteVisitor1 visitor1 = new ConcreteVisitor1(); ConcreteVisitor2 visitor2 = new ConcreteVisitor2(); objectStructure.accept(visitor1); objectStructure.accept(visitor2); } } ``` ### 优缺点 - 访问者模式的优点在于增加新的操作很容易,因为增加新的操作相当于增加一个新的访问者。 - 访问者模式将有关的行为集中到一个访问者对象中。 - 访问者模式缺点在于增加新的数据结构比较困难