《大话设计模式》读书笔记
一、简单工厂模式
1.面向对象三大基本特性:封装、继承、多态
封装:封装是指将现实世界中存在的某个客体的属性与行为绑定在一起,并放置在一个逻辑单元内。该逻辑单元负责将所描述的属性隐藏起来,外界对客体内部属性的所有访问只能通过提供的用户接口实现。这样做既可以实现对客体属性的保护作用,又可以提高软件系统的可维护性。只要用户接口不改变,任何封装体内部的改变都不会对软件系统的其他部分造成影响。
继承:继承性是子类自动共享父类数据结构和方法的机制,这是类之间的一种关系。在定义和实现一个类的时候,可以在一个已经存在的类的基础之上来进行,把这个已经存在的类所定义的内容作为自己的内容,并加入若干新的内容。
多态:多态的基础是继承。多态下,子类重写父类的方法,实际使用过程中,用父类引用变量指向子类对象,此时父类引用调用的是子类重写后的方法,由于是父类引用变量,所有无法调用子类特有的方法。如果想调用子类特有的方法,必须强制类型转换到子类。
class Person { public void speak() { System.out.println("person"); } } class Chinese extends Person { @Override public void speak() { System.out.println("Chinese"); } public void chinaNo1() { System.out.println("chinaNo1"); } } class English extends Person { @Override public void speak() { System.out.println("English"); } } public class SimpleFactory { public static void main(String args[]) { Person person1 = new Chinese(); Person person2 = new English(); person1.speak(); person2.speak(); //person1.chinaNo1(); 多态下父类引用变量无法直接调用子类特有的方法 Chinese chinese = (Chinese) person1; chinese.chinaNo1(); } }
2.简单工厂模式
import java.util.Scanner; class Operation { private double numberOne = 0; private double numberTwo = 0; public double getNumberOne() { return numberOne; } public void setNumberOne(double numberOne) { this.numberOne = numberOne; } public double getNumberTwo() { return numberTwo; } public void setNumberTwo(double numberTwo) { this.numberTwo = numberTwo; } public double getResult() throws Exception { double result = 0; return result; } } class OperationAdd extends Operation { @Override public double getResult() { return getNumberOne() + getNumberTwo(); } } class OperationSub extends Operation { @Override public double getResult() { return getNumberOne() - getNumberTwo(); } } class OperationMul extends Operation { @Override public double getResult() { return getNumberOne() * getNumberTwo(); } } class OperationDiv extends Operation { @Override public double getResult() throws Exception { if (getNumberTwo() == 0) { throw new Exception("除数不能为0"); } return getNumberOne() / getNumberTwo(); } } class OperationFactory { public static Operation createOperation(char operation) throws Exception { switch (operation) { case '+': { return new OperationAdd(); } case '-': { return new OperationSub(); } case '*': { return new OperationMul(); } case '/': { return new OperationDiv(); } default: { throw new Exception("不支持的计算类型"); } } } } public class SimpleFactory { public static void main(String args[]) { Scanner sc = new Scanner(System.in); double number1 = sc.nextDouble(); System.out.println(number1); char op = sc.next().charAt(0); System.out.println(op); double number2 = sc.nextDouble(); System.out.println(number2); Operation operation = null; try { operation = OperationFactory.createOperation(op); } catch (Exception e) { e.printStackTrace(); } operation.setNumberOne(number1); operation.setNumberTwo(number2); try { System.out.println(operation.getResult()); } catch (Exception e) { e.printStackTrace(); } } }
3.UML类图
关联(association):当一个类“知道”另一个类时,可以用关联。企鹅“知道”气候的变化。
聚合(Aggregation):表示一种弱的“拥有”关系,体现的是A对象可以包含B对象,但B对象不是A对象但一部分。一个雁群可以有多只大雁,满足聚合关系。
合成(Composition):是一种强的“拥有”关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样。
依赖(Dependency):动物依赖氧气和水,它们是依赖关系。
二、策略模式
面相对象的编程,并不是类越多越好,类的划分是为了封装,但分类但基础是抽象,具有相同属性和功能的抽象集合才是类。例如打折,打一折和打九折只是形式的不同,抽象分析出来,所有的打折方法都是一样的,所以打折算法应该是一个类。
策略模式(Strategy):它定义类算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。
1.简单工厂模式与策略模式的结合
class CashContext { private CashSuper cashSuper; public CashContext(String type) { if (type.equals("无优惠")) { cashSuper = new CashNormal(); } else if (type.equals("打八折")) { cashSuper = new CashRebate(0.8); } else if (type.equals("满300减20")) { cashSuper = new CashReturn(300, 20); } } public double getResult(double money) { return cashSuper.getResult(money); } } class CashSuper { public double getResult(double money) { double result = 0; return result; } } class CashRebate extends CashSuper { private double rebate; public CashRebate(double rebate) { this.rebate = rebate; } @Override public double getResult(double money) { return money * rebate; } } class CashNormal extends CashSuper { @Override public double getResult(double money) { return money; } } class CashReturn extends CashSuper { private double full; private double reduction; public CashReturn(double full, double reduction) { this.full = full; this.reduction = reduction; } @Override public double getResult(double money) { return money - (money >= full ? Math.floor(money / full) * reduction : 0); } } public class Strategy { public static void main(String args[]) { CashContext cashContext = new CashContext("无优惠"); System.out.println(cashContext.getResult(1000)); cashContext = new CashContext("打八折"); System.out.println(cashContext.getResult(1000)); cashContext = new CashContext("满300减20"); System.out.println(cashContext.getResult(1000)); } }
上面的例子是简单工厂模式和策略模式的结合,从上一章可以看出,简单工厂模式需要让用户知道两个类(Operation和OperationFactory),而策略模式只需要认识一个类(CashContext)就可以类,耦合度降低。这里我们在客户端实例化的是CashContext的对象,调用的是CashContext的方法getResult,这是的收费算法彻底的与客户端分离。连算法的父类CashSuper都不让客户端认识了。
2.策略模式解析
策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合[DPE]。
策略模式的Strategy类(对应例子中的CashSuper)层次为Context定义类一系列的可供重用的算法或行为。继承有助于析取出这些算法中的公共功能[DP]。
策略模式的优点是简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试[DPE]。
当不同的行为堆砌在一个类中时,就很难避免使用条件语句来选择合适的行为。将这些行为封装在一个个独立的Strategy类中,可以在使用这些行为的类中消除条件语句[DP]。
策略模式就是用来封装算法的,但在实践中,我们发现可以用它来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性[DPE]。
在基本的策略模式中,选择所用具体实现的职责由客户端对象承担,并转给策略模式的Context对象[DPE]。这本身没有解除客户端需要选择判断的压力,而策略模式与简单工厂模式结合后,选择具体实现的职责也可以由Context来承担,这就最大化地减轻了客户端的职责。
但它依然不够完美,因为在CashContext里还是用到来switch,也就是说,要增加一种算法,就必须更改switch,更好的办法是使用反射技术(注:在抽象工厂模式章节有对反射的讲解)。
三、单一职责原则
单一职责原则:就一个类而言,应该仅有一个引起它变化的原因[ASD]。
如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏[ASD]。
四、开放——封闭原则
开放——封闭原则:软件实体(类、模块、函数等等)应该可以扩展,但是不可修改[ASD]。
这个原则其实有两个特征,一个是说“对于扩展是开放的(Open for extension)”,另一个是说“对于更改是封闭的(Closed for modification)”[ASD]。
在我们最初编写代码时,假设变化不会发生。当变化发生时,我们就创建抽象来隔离以后发生的同类变化[ASD]。
面对需求,对程序的改动是通过增加新代码进行的,而不是更改现有的代码[ASD]。
开放——封闭原则是面相对象设计的核心所在。遵循这个原则可以带来面相对象技术所声称的巨大好处,也就是可维护、可扩展、可复用、灵活性好。开发人员应该仅对程序中呈现出频繁变化的那些部分作出抽象,然而,对于应用程序中的每个部分都进行抽象同样不是一个好主意。拒绝不成熟的抽象和抽象本身一样重要[ASD]。
五、依赖倒转原则
依赖倒转原则:
1.高层模块不应该依赖低层模块。两个都应该依赖抽象。
2.抽象不应该依赖细节。细节应该依赖抽象。[ASD]
不管高层模块还是低层模块,它们都依赖于抽象,具体一点就是接口或抽象类,只要接口是稳定的,那么任何一个的更改都不用担心其他收到影响,这就使得无论高层模块还是低层模块都可以很容易地被复用。
1.里氏代换原则
里氏代换原则(LSP):子类型必须能够替换掉它们的父类型。[ASD]
一个软件实体如果使用的是一个父类的话,那么一定适用于其子类,而且它察觉不出父类对象和子类对象的区别。也就是说,在软件里面,把父类都替换成它的子类,程序的行为没有变化,简单的说,子类型必须能够替换掉它们的父类型[ASD]。
只有当子类可以替换掉父类,软件单位的功能不受到影响时,父类才能真正被复用,而子类也能够在父类的基础上增加新的行为。
由于子类型的可替换性才使得使用父类类型的模块在无需求改的情况下就可以扩展。
依赖倒转其实可以说是面相对象设计的标志,用哪种语言来编写程序并不重要,如果编写时考虑的都是如何针对抽象编程而不是针对细节编程,即程序中所有的依赖关系都是终止于抽象类或者接口,那就是面相对象的设计,反之那就是过程化的设计了[ASD]。
六、装饰模式
装饰模式(Decorator):动态地给一个对象添加一些额外地职责,就增加功能来说,装饰模式比生成子类更为灵活[DP]。
Component是定义一个对象接口,可以给这些对象动态地添加职责。ConcreteComponent是定义了一个具体的对象,也可以给这个对象添加一些职责。Decorator,装饰抽象类,继承了Component,从外类来扩展Component类的功能,但对于Component来说,是无需知道Decorator的存在的。至于ConcreteDecorator就是具体的装饰对象,起到给Component添加职责的功能[DPE]。
装饰模式是利用SetComponent来对对象进行包装的。这样每个装饰对象的实现就和如何使用这个对象分开来,每个装饰对象只关心自己的功能,不需要关心如何被添加到对象链当中[DPE]。
interface Output { String output(); } class ConsoleOutput implements Output { private String input; public ConsoleOutput(String input) { this.input = input; } public String output() { return input; } } class FilterOutput implements Output { private Output component; public FilterOutput(Output output) { this.component = output; } public String output() { if (component != null) { return component.output(); } return null; } } class LowCaseOutput extends FilterOutput { public LowCaseOutput(Output output) { super(output); } @Override public String output() { return super.output().toLowerCase(); } } public class Decorator { public static void main(String args[]) { Output output = new LowCaseOutput(new ConsoleOutput("FlkJ:LKJ:LKjLKJ")); System.out.println(output.output()); } }
装饰模式是为已有功能动态添加更多功能的一种方式。
装饰模式把每个要装饰的功能放在单独的类中,并让这个类包装它所要装饰的对象,因此,当需要执行特殊行为时,客户代码就可以在运行时根据需要有选择地、按顺序地使用装饰功能包装对象类[DP]。
装饰模式的优点是把类中的装饰功能从类中搬移出去,这样可以简化原有的类。
有效地把类的核心职责和装饰功能区分开了。而且可以去除相关类中的重复的装饰逻辑。
七、代理模式
代理模式(Proxy):为其他对象提供一种代理以控制对这个对象的访问[DP]。
//同城代打 interface SameCityProxyBeat { void BeatWithBrick(); void BeatWithStick(); void BeatWithDesk(); } class Victim { private String name; public Victim(String name) { this.name = name; } public String getName() { return name; } } class Buyer implements SameCityProxyBeat { private Victim victim; public Buyer(Victim victim) { this.victim = victim; } public void BeatWithBrick() { System.out.println(String.format("用板砖打了%s一下", victim.getName())); } public void BeatWithStick() { System.out.println(String.format("用棍子打了%s一下", victim.getName())); } public void BeatWithDesk() { System.out.println(String.format("用凳子打了%s一下", victim.getName())); } } class Proxyer implements SameCityProxyBeat { private Buyer buyer; public Proxyer(Buyer buyer) { this.buyer = buyer; } public void BeatWithBrick() { buyer.BeatWithBrick(); } public void BeatWithStick() { buyer.BeatWithStick(); } public void BeatWithDesk() { buyer.BeatWithDesk(); } } public class Proxy { public static void main(String args[]) { Proxyer proxyer = new Proxyer(new Buyer(new Victim("皮皮虾"))); proxyer.BeatWithBrick(); proxyer.BeatWithStick(); proxyer.BeatWithDesk(); } }
代理模式应用场景:
1.远程代理:也就是为一个对象在不同的地址空间提供局部代表。这样可以隐藏一个对象存在于不同地址空间的事实[DP]。
2.虚拟代理:根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的真实对象[DP]。
3.安全代理:用来控制真实对象访问时的权限[DP]。
4.智能指针:当调用真实的对象时,代理处理另外一些事[DP]。
代理模式其实就是在访问对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。
八、工厂方法模式
简单工厂模式的最大优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除类与具体产品的依赖。对于前面的例子来说,我们将操作符如'+'传给工厂,工厂返回响应的实例,但这样违背了开放——封闭原则。
工厂方法模式(Factory Method):定义了一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类[DP]。
整个工厂和产品体系其实都没有修改的变化,而只是扩展的变化,这就完全符合类开放——封闭原则的精神。
interface IFactory{ IOperation createOperation(); } class IOperation{ public double numberOne = 0; public double numberTwo = 0; public double getResult(){ return 0; } } class AddOperationFactory implements IFactory{ public IOperation createOperation() { return new AddOperation(); } } class SubOperationFactory implements IFactory{ public IOperation createOperation() { return new SubOperation(); } } class AddOperation extends IOperation{ public double getResult() { return numberOne + numberTwo; } } class SubOperation extends IOperation{ public double getResult() { return numberOne - numberTwo; } } public class Factory { public static void main(String[] args){ IFactory factory = new AddOperationFactory(); IOperation addOperation1 = factory.createOperation(); IOperation addOperation2 = factory.createOperation(); addOperation1.numberOne = 1; addOperation1.numberTwo = 1; System.out.println(addOperation1.getResult()); } }
九、原型模式
原型模式(Prototype):用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象[DP]。
原型模式其实就是从一个对象再创建另外一个可定制的对象,而且不需知道任何创建的细节。
一般在初始化的信息不发生变化的情况下,克隆是最好的办法。这既隐藏类细节,又对性能是大大的提高。
不用重新初始化对象,而是动态地获得对象运行时的状态。
1.深拷贝与浅拷贝
浅拷贝:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。
深拷贝:把要复制的对象所引用的对象都复制一遍。深拷贝把引用对象的变量指向复制过的新对象,而不是原有的被引用的对象。
class WorkExperience implements Cloneable { private String workDate; private String company; public String getWorkDate() { return workDate; } public void setWorkDate(String workDate) { this.workDate = workDate; } public String getCompany() { return company; } public void setCompany(String company) { this.company = company; } @Override public String toString() { return "WorkExperience{" + "workDate='" + workDate + '\'' + ", company='" + company + '\'' + '}'; } @Override protected Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } } class Resume implements Cloneable { private String name; private String sex; private String age; private WorkExperience workExperience; public void setPersonalInfo(String sex, String age){ this.sex = sex; this.age = age; } public void setWorkExperience(String workDate, String company) { this.workExperience.setWorkDate(workDate); this.workExperience.setCompany(company); } public Resume() { } public Resume(String name) { this.name = name; this.workExperience = new WorkExperience(); } @Override public String toString() { return "Resume{" + "name='" + name + '\'' + ", sex='" + sex + '\'' + ", age='" + age + '\'' + ", workExperience=" + workExperience + '}'; } @Override protected Object clone() { Resume resume = new Resume(); resume.name = this.name; resume.sex = this.sex; resume.age = this.age; resume.workExperience = (WorkExperience) this.workExperience.clone(); return resume; } } public class Prototype { public static void main(String[] args){ Resume resume = new Resume("小明"); resume.setPersonalInfo("男", "12"); resume.setWorkExperience("2019-03-21", "字节跳动"); Resume resume1 = (Resume) resume.clone(); resume1.setPersonalInfo("女", "13"); resume1.setWorkExperience("2018-08-08", "网易游戏"); System.out.println(resume); System.out.println(resume1); } }
十、模版方法模式
模版方法模式:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模版方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
模版方法模式用到了继承,父类成为子类的模版,所有重复的代码都应该要上升到父类去,而不是让每个子类都去重复。
当我们要完成在某一细节层次一致的一个过程或一系列步骤,但其个别步骤在更详细但层次上的实现可能不同时,我们通常考虑用模版方法模式来处理。
模版方法模式是通过把不变形为搬移到超类,去除子类中的重复代码来体现它的优势。
模版方法模式就是提供来一个很好的代码复用平台。
当不变的和可变的行为在方法的子类实现中混合在一起的时候,不变的行为就会在子类中重复出现。我们通过模版方法模式把这些行为搬移到单一的地方,这样就帮助子类摆脱重复的不变行为的纠缠。
abstract class Story { public abstract String getNameOne(); public abstract String getNameTwo(); public abstract String getPostion(); public abstract String getTalk(); public void tellStory() { System.out.println(String.format("有一天,%s在%s对%s说:%s", getNameOne(), getPostion(), getNameTwo(), getTalk())); } } class HappyStory extends Story{ @Override public String getNameOne() { return "小明"; } @Override public String getNameTwo() { return "小芳"; } @Override public String getPostion() { return "学校"; } @Override public String getTalk() { return "我喜欢你"; } } class SadStory extends Story{ @Override public String getNameOne() { return "小芳"; } @Override public String getNameTwo() { return "小明"; } @Override public String getPostion() { return "家门前"; } @Override public String getTalk() { return "分手吧!"; } } public class Template { public static void main(String[] args){ Story story = new HappyStory(); story.tellStory(); story = new SadStory(); story.tellStory(); } }
十一、迪米特法则
迪米特法则(LoD,也叫最少知识原则[J&DP]):如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法对话,可以通过第三者转发这个调用[J&DP]。
迪米特法则首先强调对前提是在类对结构设计上,每一个类都应当尽量降低成员对访问权限[J&DP],也就是说,一个类包装好自己对private状态,不需要让别的类知道的字段或行为就不要公开。
迪米特法则其根本思想,是强调了类之间的松耦合。
类之间的耦合越弱,越有利于复用,一个处在若耦合的类被修改,不会对有关系对类造成波及。
十二、外观模式
外观模式(Facade):为子系统中对一组接口提供一个一致对界面,此模式定义类一个高层接口,这个接口使得这个子系统更加容易使用[DP]。
外观模式在什么时候使用最好呢?
这要分三个阶段来说,首先,在设计模式初期阶段,应该要有意识对将不同的两个层分离,比如经典对三层架构,就需要考虑在数据访问层和业务逻辑层、业务逻辑层和表示层的层与层之间建立外观Facade,这样可以为复杂对子系统提供一个简单对接口,使得耦合大大降低。其次,在开发阶段,子系统往往因为不断的重构演化而变得越来越复杂,大多数对模式使用时也都会产生很多很小对类,这本是好事,但也给外部调用他们的用户程序带来类使用上的困难,增加外观Facade可以提供一个简单的接口,减少它们之间的依赖。第三,在维护一个遗留的大型系统时,可能这个系统已经非常难以维护和扩展了,但因为它包含非常重要的功能,新的需求开发必须要依赖于它。此时用外观模式Facade也是非常合适的。你可以为新系统开发一个外观Facade类,来提供设计粗糙或者高度复杂的遗留代码的比较清晰简单的接口,让新系统与Facade对象交互,Facade与遗留代码交互所有复杂的工作[R2P]。
class SubSystemOne{ public void methodOne(){ System.out.println("methodOne"); } } class SubSystemTwo{ public void methodTwo(){ System.out.println("SubsystemTwo"); } } class SubSystemThree{ public void methodThree(){ System.out.println("methodThree"); } } class SubSystemFour{ public void methodFour(){ System.out.println("methodFour"); } } public class Facade { private SubSystemOne subSystemOne; private SubSystemTwo subSystemTwo; private SubSystemThree subSystemThree; private SubSystemFour subSystemFour; public Facade(){ this.subSystemOne = new SubSystemOne(); this.subSystemTwo = new SubSystemTwo(); this.subSystemThree = new SubSystemThree(); this.subSystemFour = new SubSystemFour(); } public void MethodA(){ subSystemOne.methodOne(); subSystemThree.methodThree(); subSystemTwo.methodTwo(); } public void MethodB(){ subSystemThree.methodThree(); subSystemFour.methodFour(); } public static void main(String[] args){ Facade facade = new Facade(); facade.MethodA(); facade.MethodB(); } }
十三、建造者模式
建造者模式(Builder):将一个复杂对象的构建与它的表示分离,使得同样的构造过程可以创建不同的表示[DP]。
Builder:是为创建一个Product对象的各个部件指定的抽象接口。
ConcreteBuilder:具体建造者,实现Builder接口,构造和装配各个部件。
Product:具体的产品角色。
Director:构建一个使用Builder接口的对象。
建造者模式使用时机:主要是用于创建一些复杂的对象,这些对象内部构建间的建造顺序通常是稳定的,但对象内部的构建通常面临着复杂的变化。
建造者模式的好处就是使得建造代码与表示代码分离,由于建造者隐藏了该产品是如何组装的,所以若需要改变一个产品的内部表示,只需要再定义一个具体的建造者就可以了。
import java.util.LinkedList; import java.util.List; class Product{ List<String> parts = new LinkedList<String>(); public void add(String part){ parts.add(part); } public void show(){ System.out.println(parts.toString()); } } interface IBuilder{ void buildPartOne(); void buildPartTwo(); Product getResult(); } class ConcreteBuilderOne implements IBuilder{ private Product product = new Product(); public void buildPartOne() { product.add("ConcreteBuilderOne partOne"); } public void buildPartTwo() { product.add("ConcreteBuilderOne partTwo"); } public Product getResult() { return product; } } class ConcreteBuilderTwo implements IBuilder{ private Product product = new Product(); public void buildPartOne() { product.add("ConcreteBuilderTwo partOne"); } public void buildPartTwo() { product.add("ConcreteBuilderTwo partTwo"); } public Product getResult() { return product; } } class Director{ public void createProduct(IBuilder builder){ builder.buildPartOne(); builder.buildPartTwo(); } } public class Builder { public static void main(String[] args){ Director director = new Director(); IBuilder builder1 = new ConcreteBuilderOne(); IBuilder builder2 = new ConcreteBuilderTwo(); director.createProduct(builder1); director.createProduct(builder2); Product p1 = builder1.getResult(); Product p2 = builder2.getResult(); p1.show(); p2.show(); } }
建造者模式是在当创建复杂对象当算法应该独立于该对象当组成部分以及它们当装配方式时适用当模式。
十四、观察者模式
观察者模式:又叫做发布——订阅(Publish/Subscribe)模式,定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己[DP]。
import java.util.LinkedList; import java.util.List; //通知者接口 interface ISubject { abstract void attach(IObserver observer); abstract void detach(IObserver observer); abstract void Notify(); } class ConcreteSubject implements ISubject { private List<IObserver> observers = new LinkedList<IObserver>(); private String message; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public void attach(IObserver observer) { observers.add(observer); } public void detach(IObserver observer) { observers.remove(observer); } public void Notify() { for (IObserver observer : observers) { observer.update(); } } } interface IObserver { void update(); } class ConcreteObserver implements IObserver { private ConcreteSubject subject; private String name; private String message; public ConcreteObserver(ConcreteSubject subject, String name) { this.subject = subject; this.name = name; } public void update() { this.message = subject.getMessage(); System.out.println(name + ":" + message); } } public class Observer { public static void main(String[] args){ ConcreteSubject subject = new ConcreteSubject(); IObserver observer1 = new ConcreteObserver(subject, "小明"); IObserver observer2 = new ConcreteObserver(subject, "小红"); subject.attach(observer1); subject.attach(observer2); subject.setMessage("老师来了"); //subject.detach(observer1); subject.Notify(); } }
观察者模式动机:将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便[DP]。
使用时机:当一个对象的改变需要同时改变其他对象的时候。
而且它不知道具体有多少对象有待改变时,应该考虑使用观察者模式。
一个抽象模型有两个方面,其中一个方面依赖于另一方面,这时使用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。
总的来说,观察者模式所做的工作其实就是在解除耦合。让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响另一边的变化。
1.观察者模式的不足
不足:“抽象通知者”依赖“抽象观察者”,也就是说,万一没有类抽象观察者这样的接口,通知的功能就完不成了。
用委托进行改造:
委托delegate在C#中属于基本特性,在Java中没有委托,但可以用反射实现观察者模式的委托。
十五、抽象工厂模式
抽象工厂模式(Abstract Factory):提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类[DP]。
好处:
1.易于交换产品系列,由于具体工厂类,在一个应用中只需要在初始化的时候出现一次,这就使得改变一个应用的具体工厂变得非常容易,他只需要改变具体工厂即可使用不同的产品配置。
2.它让具体的创建实例过程与客户端分离,客户端是通过它们的抽象接口操作实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户代码中。
缺点:新增功能要增加的类太多
1.用简单工厂来改进抽象工厂
2.用反射+抽象工厂的数据访问程序
由于简单工厂方法是在工厂中添加switch语句,导致如果要新增一个种类就必须在switch中增加一个case,不利于维护,因此采用反射来解决。
class User { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } class Production { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } interface IUser { void insert(); void getUser(); } interface IProduction { void insert(Production production); Production getProduction(); } class SQLServerUser implements IUser { public void insert() { System.out.println("SqlserverUser 插入记录"); } public void getUser() { System.out.println("SqlserverUser 查询记录"); } } class AccessUser implements IUser { public void insert() { System.out.println("AccessUser 插入记录"); } public void getUser() { System.out.println("AccessUser 查询记录"); } } class SimpleFactory1 { // private static final String db = "SQLServer"; private static final String db = "Access"; IUser createUser() { try { Class<?> clazz = Class.forName(db + "User"); return (IUser) clazz.newInstance(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return null; } } public class AbstractFactory { public static void main(String[] args) { SimpleFactory1 simpleFactory = new SimpleFactory1(); IUser user = simpleFactory.createUser(); user.insert(); user.getUser(); } }
3.用反射+配置文件实现数据访问程序
由于配置文件不是重点,这里就不讨论了,但为了保证原书的完整性,还是将其列了出来。
十六、状态模式
状态模式(State):当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类[DP]。
状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。
好处:将与特定状态相关的行为局部化,并且将不同状态的行为分割开来。
将特定的状态相关的行为都放入一个对象中,由于所有与状态相关的代码都存在于某一个ConcreteState中,所以通过定义新的子类可以很容易地增加新的状态和转换[DP]。
消除庞大的条件分支语句。
状态模式通过把各种状态转移逻辑分布到State的子类之间,来减少相互间的依赖。
何时使用状态模式:当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为,就可以考虑使用状态模式。
class Work{ private IState state; private int hour; private boolean finish; public IState getState() { return state; } public void setState(IState state) { this.state = state; } public int getHour() { return hour; } public void setHour(int hour) { this.hour = hour; } public boolean getFinish() { return finish; } public void setFinish(boolean finish) { this.finish = finish; } public void writeProgram(){ state.writeProgram(this); } } interface IState{ void writeProgram(Work work); } class ForenoonState implements IState{ public void writeProgram(Work work) { if(work.getHour()<12) { System.out.println(work.getHour() + " 早晨精力充沛"); }else{ work.setState(new AfternoonState()); work.writeProgram(); } } } class AfternoonState implements IState{ public void writeProgram(Work work) { if(work.getHour()<18){ System.out.println(work.getHour()+" 在中午的困倦中慢慢苏醒"); }else{ work.setState(new EveningState()); work.writeProgram(); } } } class EveningState implements IState{ public void writeProgram(Work work) { if(work.getFinish()){ work.setState(new HomeState()); work.writeProgram(); }else{ if(work.getHour()<21){ System.out.println(work.getHour()+" 加班哦..."); }else{ work.setState(new SleepState()); work.writeProgram(); } } } } class SleepState implements IState{ public void writeProgram(Work work) { System.out.println(work.getHour()+" 睡着了..."); } } class HomeState implements IState{ public void writeProgram(Work work) { System.out.println(work.getHour()+" 在家舒服的一批"); } } public class State { public static void main(String[] args){ Work work = new Work(); work.setState(new ForenoonState()); work.setHour(9); work.writeProgram(); work.setHour(13); work.writeProgram(); work.setHour(17); // work.setFinish(true); work.setHour(19); work.writeProgram(); work.setHour(22); work.writeProgram(); } }
十七、适配器模式
适配器模式(Adapter):将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作[DP]。
系统的数据和行为都正确,但接口不符时,我们应该考虑用适配器,目的是使控制范围之外的一个原有对象与某个接口匹配。适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况。
1.何时使用?
使用一个已经存在的类,但如果它的接口,也就是它的方法和你的要求不相同时,就应该考虑用适配器模式。
两个类所做的事情相同或相似,但是具有不同的接口时要使用它。
在双方都不太容易修改的时候再使用适配器模式。
如果能事先预防接口不同的问题,不匹配问题就不会发生;在有小的接口不统一问题发生时,及时重构,问题不至于扩大;只有碰到无法改变原有设计和代码的情况时,才考虑适配。
十八、备忘录模式
备忘录模式(Memento):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态[DP]。
要保存的细节给封装在了Memento中了,哪一天要更改保存的细节也不用影响客户端了。
Memento模式比较适用于功能比较复杂的,但需要维护或记录属性历史的类,或者需要保存的属性只是众多属性中的一小部分时,Originator可以根据保存的Memento信息还原到前一状态。
使用备忘录可以把复杂的对象内部信息对其他的对象屏蔽起来[DP]。
当角色的状态改变的时候,有可能这个状态无效,这时候就可以使用暂时存储起来的备忘录将状态复原[DP]。
class Player { private int hp; private int mp; private int level; public Player() { hp = 100; mp = 100; level = 1; } public Memonto createMemonte() { return new Memonto(hp, mp, level); } public void setMemonto(Memonto memonto) { System.out.println("回档"); this.hp = memonto.getHp(); this.mp = memonto.getMp(); this.level = memonto.getLevel(); } public void toBeDie() { hp = 1; mp = 1; System.out.println("to be die"); } public void show() { System.out.println(String.format("hp=%d, mp=%d, level=%d", hp, mp, level)); } } public class Memonto { private int hp; private int mp; private int level; public int getHp() { return hp; } public void setHp(int hp) { this.hp = hp; } public int getMp() { return mp; } public void setMp(int mp) { this.mp = mp; } public int getLevel() { return level; } public void setLevel(int level) { this.level = level; } public Memonto(int hp, int mp, int level) { this.hp = hp; this.mp = mp; this.level = level; } public static void main(String[] args) { Player player = new Player(); player.show(); Caretaker caretaker = new Caretaker(); caretaker.setMemonto(player.createMemonte()); player.toBeDie(); player.show(); player.setMemonto(caretaker.getMemonto()); player.show(); } } class Caretaker { private Memonto memonto; public Memonto getMemonto() { return memonto; } public void setMemonto(Memonto memonto) { this.memonto = memonto; } }
十九、组合模式
组合模式(Composite):将对象组合成树形结构以表示“部分—整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性[DP]。
import java.util.LinkedList; import java.util.List; abstract class Company { String name; public Company(String name) { this.name = name; } public abstract void add(Company company); public abstract void remove(Company company); public abstract void display(int depth); public abstract void lineOfDuty(); } class ConcreteCompany extends Company { private List<Company> children = new LinkedList<>(); public ConcreteCompany(String name) { super(name); } @Override public void add(Company company) { children.add(company); } @Override public void remove(Company company) { children.remove(company); } @Override public void display(int depth) { StringBuilder prefix = new StringBuilder(); for (int i = 0; i < depth; i++) { prefix.append('-'); } prefix.append(name); System.out.println(prefix); for (Company company : children) { company.display(depth + 2); } } @Override public void lineOfDuty() { System.out.println(String.format("%s 牛批", name)); for (Company company : children) { company.lineOfDuty(); } } } class HRDepartment extends Company { public HRDepartment(String name) { super(name); } @Override public void add(Company company) { } @Override public void remove(Company company) { } @Override public void display(int depth) { StringBuilder prefix = new StringBuilder(); for (int i = 0; i < depth; i++) { prefix.append('-'); } prefix.append(name); System.out.println(prefix); } @Override public void lineOfDuty() { System.out.println(String.format("%s 员工招聘培训管理", name)); } } class FinanceDepartment extends Company { public FinanceDepartment(String name) { super(name); } @Override public void add(Company company) { } @Override public void remove(Company company) { } @Override public void display(int depth) { StringBuilder prefix = new StringBuilder(); for (int i = 0; i < depth; i++) { prefix.append('-'); } prefix.append(name); System.out.println(prefix); } @Override public void lineOfDuty() { System.out.println(String.format("%s 公司财务收支管理", name)); } } public class Composite { public static void main(String[] args) { Company root = new ConcreteCompany("全球总部"); Company rootHr = new HRDepartment("全球总部HR"); root.add(rootHr); Company rootFa = new FinanceDepartment("全球总部财政"); root.add(rootFa); Company com1 = new ConcreteCompany("中国总部"); root.add(com1); Company com1Hr = new HRDepartment("中国总部HR"); com1.add(com1Hr); Company com1Fa = new FinanceDepartment("中国总部财政"); com1.add(com1Fa); Company com2 = new ConcreteCompany("杭州总部"); com1.add(com2); Company com2Hr = new HRDepartment("杭州总部HR"); com2.add(com2Hr); Company com2Fa = new FinanceDepartment("杭州总部财政"); com2.add(com2Fa); root.display(2); root.lineOfDuty(); } }
1.何时使用组合模式?
当你发现需求中是体现部分与整体层次的结构时,以及你希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时,就应该考虑用组合模式了。
2.好处
组合模式定义了包含人力资源部和财务部这些基本对象和分公司、办事处等组合对象的类层次结构。基本对象可以被组合成更复杂的组合对象,而这个组合对象又可以被组合,这样不断地递归下去,客户代码中,任何用到基本对象的地方都可以使用组合对象了。
用户不用关心到底是处理一个叶节点还是处理一个组合组建,也就用不着为定义组合而写一些选择判断语句了。
组合模式让客户可以一致地使用组合结构和单个对象。
二十、迭代器模式
迭代器模式(Iterator):提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示[DP]。
当你需要访问一个聚集对象,而且不管这些对象是什么都需要遍历的时候,你就应该考虑用迭代器模式。
你需要对聚集有多种方式遍历时,可以考虑用迭代器模式。
为遍历不同的聚集结构提供如开始、下一个、是否结束、当前哪一项等统一的结构。
迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器来负责,这样既可以做到不暴露集合的内部结构,又可以让外部代码透明地访问集合内部的数据。
import java.util.LinkedList; import java.util.List; interface Iterator { public abstract Object first(); public abstract Object next(); public abstract boolean isDone(); public abstract Object currentItem(); } interface Aggregate { public abstract Iterator createIterator(); } class ConcreteAggregate implements Aggregate { private List<Object> items = new LinkedList<>(); @Override public Iterator createIterator() { return new ConcreteIterator(this); } public Object get(int index) { return items.get(index); } public void add(Object obj) { items.add(obj); } public Integer size() { return items.size(); } } class ConcreteIterator implements Iterator { private ConcreteAggregate aggregate; private int current = 0; public ConcreteIterator(ConcreteAggregate aggregate) { this.aggregate = aggregate; } @Override public Object first() { return aggregate.get(0); } @Override public Object next() { Object res = null; current++; if (current < aggregate.size()) { res = aggregate.get(current); } return res; } @Override public boolean isDone() { return current >= aggregate.size(); } @Override public Object currentItem() { return aggregate.get(current); } } public class MyIterator { public static void main(String[] args) { ConcreteAggregate aggregate = new ConcreteAggregate(); aggregate.add("c1"); aggregate.add("c2"); aggregate.add("c3"); aggregate.add("c4"); aggregate.add("c5"); aggregate.add("c6"); Iterator iterator = aggregate.createIterator(); while (!iterator.isDone()) { System.out.println(iterator.currentItem()); iterator.next(); } } }
二十一、单例模式
单例模式(Singleton):保证一个类仅有一个实例,并提供一个访问它的全局访问点。
通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。一个最好的办法就是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法[DP]。
单例模式因为Singleton类封装它的唯一实例,这样它可以严格地控制客户怎样访问它以及何时访问它。简单地说就是对唯一实例的受控访问。
1.单例模式VS实用类的静态方法
实用类不保存状态,仅提供一些静态方法或静态属性让你使用,而单例类是有状态的。实用类不能用于继承多态,而单例虽然实例唯一,却是可以有子类来继承。实用类只不过是一些方法属性的集合,而单例却是有着唯一的对象实例。
2.多线程时的单例
多线程中,调用GetInstance()方法,会有可能造成多个实例。
可以使用lock(Java可以使用synchronized)
public class Singleton { private static Singleton instance; private static final Object syncRoot = new Object(); private Singleton() { } public static Singleton GetInstance() { synchronized (syncRoot) { if (instance == null) { instance = new Singleton(); } } return instance; } }
这样的话会导致每次调用Singleton实例都会进行一次加锁。使用双重锁定来避免。
3.双重锁定
public class Singleton { private static Singleton instance; private static final Object syncRoot = new Object(); private Singleton() { } public static Singleton GetInstance() { if (instance == null) { synchronized (syncRoot) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
二十二、桥接模式
对象的继承关系时在编译时就定义好了,所以无法在运行时改变从父类继承的实现。子类的实现与它的父类有着非常紧密的依赖关系,以至于父类实现中的任何变化必然会导致子类发生变化。当你需要复用子类时,如果继承下来的实现不适合解决新的问题,则父类必须重写或被其他更合适的类替换。这种依赖关系限制了灵活性并最终限制了复用性[DP]。
桥接模式(Bridge):将抽象部分与它的实现部分分离,使它们都可以独立地变化[DP]。
这里需要理解一下,什么叫抽象与它的实现分离,这并不是说,让抽象类与其派生类分离,因为这没有任何意义。实现指的是抽象类和它的派生类用来实现自己的对象[DPE]。
1.合成/聚合复用原则
合成/聚合复用原则(CARP):尽量使用合成/聚合,尽量不要使用类继承[J&DP]。
聚合表示一种弱的“拥有”关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分。
合成则是一种强的“拥有”关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样[DPE]。
好处是优先使用对象的合成/聚合将有助于你保持每个类被封装,并被集中在单个任务上。这样类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物[DP]。
实现系统可能有多角度分类,每一种分类都有可能变化,那么就把这种多角度分离出来让它们独立变化,减少它们之间的耦合。
abstract class Implementor { public abstract void operation(); } class ImplementorA extends Implementor { @Override public void operation() { System.out.println("implement A"); } } class ImplementorB extends Implementor { @Override public void operation() { System.out.println("implement B"); } } abstract class Abstraction { protected Implementor implementor; public void setImplementor(Implementor implementor) { this.implementor = implementor; } public void operation() { implementor.operation(); } } class RefinedAbstraction extends Abstraction { @Override public void operation() { implementor.operation(); } } public class Bridge { public static void main(String[] args) { Abstraction abstraction = new RefinedAbstraction(); Implementor implementorA = new ImplementorA(); Implementor implementorB = new ImplementorB(); abstraction.setImplementor(implementorA); abstraction.operation(); abstraction.setImplementor(implementorB); abstraction.operation(); } }
二十三、命令模式
命令模式(Command):将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作[DP]。
1.优点
1.它能较容易的设计一个命令队列。
2.在需要的情况下,可以较容易地将命令记入日志。
3.允许接受请求的一方决定是否要否决请求。
4.可以容易地实现对请求的撤销和重做。
5.由于加进新的具体命令类不影响其他的类,因此增加新的具体命令类很容易。
6.命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分割开[DP]。
敏捷开发原则告诉我们,不要为代码添加基于猜测的、实际不需要的功能。如果不清楚一个系统是否需要命令模式,一般就不要着急去实现它,事实上,在需要的时候通过重构实现这个模式并不困难,只有在真正需要如撤销/恢复操作等功能时,把原来的代码重构为命令模式才有意义[R2P]。
import java.util.LinkedList; import java.util.List; class Receiver { public void actionOne() { System.out.println("执行请求1"); } public void actionTwo() { System.out.println("执行请求2"); } } class Invoker { List<Command> commands = new LinkedList<>(); public void setCommand(Command command) { commands.add(command); } public void cancelCommand(Command command) { commands.remove(command); } public void Notify() { for (Command c : commands) { c.executeCommand(); } } } public abstract class Command { protected Receiver receiver; public Command(Receiver receiver) { this.receiver = receiver; } public abstract void executeCommand(); public static void main(String[] args) { Invoker invoker = new Invoker(); Receiver receiver = new Receiver(); Command commandOne = new CommandOne(receiver); Command commandTwo = new CommandTwo(receiver); invoker.setCommand(commandOne); invoker.setCommand(commandTwo); invoker.cancelCommand(commandOne); invoker.Notify(); } } class CommandOne extends Command { public CommandOne(Receiver receiver) { super(receiver); } @Override public void executeCommand() { receiver.actionOne(); } } class CommandTwo extends Command { public CommandTwo(Receiver receiver) { super(receiver); } @Override public void executeCommand() { receiver.actionTwo(); } }
二十四、职责链模式
职责链模式(Chain of Responsibility):使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
1.好处
当客户提交一个请求时,请求时沿链传递直至有一个ConcreteHandler对象负责处理它[DP]。
接收者和发送者都没有对方的明确信息,且链中的对象自己也并不知道链的结构。结果是职责链可简化对象的相互连接,它们仅需保持一个指向其后继者的引用,而不需保持它所有的候选接收者的引用[DP]。
随时地增加或修改处理一个请求的结构。增强了给对象指派职责的灵活性[DP]。
一个请求极有可能到了链的末端都得不到处理,或者因为没有正确配置而得不到处理。
class Request { private String type; private String content; public Request(String type, String content) { this.type = type; this.content = content; } public String getType() { return type; } public String getContent() { return content; } } abstract class Manager { private String name; protected Manager superior; public Manager(String name) { this.name = name; } public void setSuperior(Manager superior) { this.superior = superior; } public abstract void handleRequest(Request request); } class MainProgrammer extends Manager { public MainProgrammer(String name) { super(name); } @Override public void handleRequest(Request request) { if (request.getType().equals("请假")) { System.out.println(String.format("%s %s 同意了", request.getType(), request.getContent())); } else { System.out.println(String.format("作为一个主程,我不能处理%s", request.getType())); superior.handleRequest(request); } } } class CommonManager extends Manager { public CommonManager(String name) { super(name); } @Override public void handleRequest(Request request) { if (request.getType().equals("请长假")) { System.out.println(String.format("%s %s 同意了", request.getType(), request.getContent())); } else { System.out.println(String.format("作为一个经理,我不能处理%s", request.getType())); superior.handleRequest(request); } } } class Boss extends Manager { public Boss(String name) { super(name); } @Override public void handleRequest(Request request) { if (request.getType().equals("加薪")) { System.out.println(String.format("%s %s 同意了", request.getType(), request.getContent())); } } } public class ResponsibilityChain { public static void main(String[] args) { MainProgrammer mainProgrammer = new MainProgrammer("主程"); CommonManager commonManager = new CommonManager("经理"); Boss boss = new Boss("老板"); Request request = new Request("加薪", "大哥想加薪"); mainProgrammer.superior = commonManager; commonManager.superior = boss; mainProgrammer.handleRequest(request); } }
二十五、中介者模式
中介者模式(Mediator):用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互[DP]。
尽管将一个系统分割成许多对象通常可以增加其可复用性,但是对象间相互连接的激增又会降低其可复用性了。
大量的连接使得一个对象不可能在没有其他对象的支持下工作,系统表现为一个不可分割的整体,所以,对系统的行为进行任何较大的改动就十分困难了。
abstract class UnitedNation { public abstract void declare(String message, Country colleague); } abstract class Country { protected UnitedNation mediator; private String name; public Country(String name, UnitedNation mediator) { this.name = name; this.mediator = mediator; } public abstract void declare(String message); public abstract void Notify(String message); } class Chinese extends Country { public Chinese(String name, UnitedNation mediator) { super(name, mediator); } @Override public void declare(String message) { mediator.declare(message, this); } @Override public void Notify(String message) { System.out.println(message); } } class Japanese extends Country { public Japanese(String name, UnitedNation mediator) { super(name, mediator); } @Override public void declare(String message) { mediator.declare(message, this); } @Override public void Notify(String message) { System.out.println(message); } } class UnitedNationSecurityCouncil extends UnitedNation { private Country chinese; private Country japanese; public void setChinese(Country chinese) { this.chinese = chinese; } public void setJapanese(Country japanese) { this.japanese = japanese; } @Override public void declare(String message, Country colleague) { if (colleague == chinese) { japanese.Notify("小日本收到消息: " + message); } else if (colleague == japanese) { chinese.Notify("大中国收到消息: " + message); } } } public class Mediator { public static void main(String[] args) { UnitedNationSecurityCouncil unitedNation = new UnitedNationSecurityCouncil(); Country chinese = new Chinese("Chinese", unitedNation); Country japanese = new Japanese("Japanese", unitedNation); unitedNation.setChinese(chinese); unitedNation.setJapanese(japanese); chinese.declare("钓鱼岛是中国的!"); japanese.declare("是的,钓鱼岛是中国的!"); } }
中介者模式很容易在系统中应用,也很容易在系统中误用。当系统出现了“多对多”交互复杂的对象群时,不要急于使用中介者模式,而要先反思你的系统在设计上是不是合理。
1.优点
Mediator的出现减少了各个Colleague的耦合,使得可以独立地改变和复用各个Colleague类和Mediator。
由于把对象如何协作进行了抽象,将中介作为一个独立的概念并将其封装在一个对象中,这样关注的对象就从对象各自本身的行为转移到它们之间的交互上来,也就是站在一个更宏观的角度去看待系统。
2.缺点
由于ConcreteMediator控制了集中化,于是就把交互复杂性变为了中介者的复杂性,这就使得中介者会变得比任何一个ConcreteColleague都复杂。
中介者模式一般应用于一组对象以定义良好但是复杂的方式进行通信的场合,以及想定制一个分布在多个类中的行为,而又不想生成太多的子类的场合。
二十六、享元模式
享元模式(Flyweight):运用共享技术有效地支持大量细粒度的对象。
享元模式可以避免大量非常相似类的开销。在程序设计中,有时需要生成大量细粒度的类实例来表示数据。如果能发现这些实例除了几个参数外基本上都是相同的,有时就能够大幅度地减少需要实例化的类的数量。如果能把哪些参数移到类实例的外面,在方法调用时将它们传递进来,就可以通过共享大幅度地减少单个实例的数目。
如果一个应用程序使用了大量的对象,而大量的这些对象造成了很大的存储开销时及应该考虑使用;
还有就是对象的大多数状态可以外部状态,如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象,此时可以考虑使用享元模式。
import java.util.Hashtable; class User{ private String name; public User(String name) { this.name = name; } public String getName() { return name; } } abstract class Website{ protected String name; public Website(String name) { this.name = name; } public abstract void use(User user); } class ConcreteWebsite extends Website{ public ConcreteWebsite(String name) { super(name); } @Override public void use(User user) { System.out.println(String.format("%s %s", name, user.getName())); } } class WebsiteFactory{ private Hashtable websites = new Hashtable(); public Website getWebsite(String type){ if(!websites.contains(type)){ websites.put(type, new ConcreteWebsite(type)); } return (Website) websites.get(type); } } public class Flyweight { public static void main(String[] args){ WebsiteFactory factory = new WebsiteFactory(); Website w1 = factory.getWebsite("blog"); w1.use(new User("xiaoming")); Website w2 = factory.getWebsite("blog"); w2.use(new User("xiaohong")); Website w3 = factory.getWebsite("notBlog"); w3.use(new User("xiaolan")); Website w4 = factory.getWebsite("notBlog"); w4.use(new User("xiaolv")); } }
二十七、解释器模式
解释器模式(interpreter):给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子[DP]。
如果一种特定类型的问题发生的频率足够高,那么可能就值得将问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题[DP]。
二十八、访问者模式
访问者模式(Visitor):表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作[DP]。
访问者模式适用于数据结构相对稳定的系统。
他把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由地演化。
1.目的
访问者模式的目的是要把处理从数据结构分离出来。
如果系统有比较稳定的数据结构,又有易于变化的算法的话,使用访问者模式就是比较合适的,因为访问者模式使得算法操作的增加变得容易。
2.优点
访问者模式就是增加新的操作很容易,因为增加新的操作就意味着增加一个新的访问者。访问者模式将有关的行为集中到一个访问者对象中。
3.缺点
增加新的数据结构变得困难了。
import java.util.LinkedList; import java.util.List; abstract class Person { public abstract void getConclusion(Action action); } class Man extends Person { @Override public void getConclusion(Action action) { action.getManConclusion(this); } } class Woman extends Person { @Override public void getConclusion(Action action) { action.getWomenConclusion(this); } } abstract class Action { public abstract void getManConclusion(Man man); public abstract void getWomenConclusion(Woman woman); } class Success extends Action { @Override public void getManConclusion(Man man) { System.out.println(String.format("%s成功时,背后一定有个伟大的女人", man.getClass().toString())); } @Override public void getWomenConclusion(Woman woman) { System.out.println(String.format("%s成功时,背后一定有个窝囊的男人", woman.getClass().toString())); } } class Marriage extends Action { @Override public void getManConclusion(Man man) { System.out.println(String.format("%s结婚后,有妻徒刑", man.getClass().toString())); } @Override public void getWomenConclusion(Woman woman) { System.out.println(String.format("%s结婚后,得到保障", woman.getClass().toString())); } } class ObjectStructure { private List<Person> elements = new LinkedList<>(); public void add(Person person) { elements.add(person); } public void remove(Person person) { elements.remove(person); } public void display(Action action) { for (Person p : elements) { p.getConclusion(action); } } } public class Visitor { public static void main(String[] args) { ObjectStructure objectStructure = new ObjectStructure(); Man man = new Man(); Woman woman = new Woman(); objectStructure.add(man); objectStructure.add(woman); objectStructure.display(new Success()); objectStructure.display(new Marriage()); } }