正好研究生开了这门课,叫做高级软件设计。本人虽然了解c++,但是不熟,老师上课讲的很深,java的设计模式比较熟,所以听得很懂。同时呢,老师上课还讲了C++的一些经典设计模式的实现(好吧,实际上是大部分),但是我这个时候基本神游天外了。幸运的是,考试只考了java版本的,哈哈。然后考前整理了下知识,发表到博客上,供大家参考
定义:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。
问题由来:在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误,也可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试。
解决方案:当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
定义一:如果对每一个类型为 T1的对象 o1,都有类型为 T2 的对象o2,使得以 T1定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1 的子类型。
定义2:所有引用基类的地方必须能透明地使用其子类的对象。3、依赖倒转原则(DependenceInversion Principle)
问题由来:有一功能P1,由类A完成。现需要将功能P1进行扩展,扩展后的功能为P,其中P由原有功能P1与新功能P2组成。新功能P由类A的子类B来完成,则子类B在完成新功能P2的同时,有可能会导致原有功能P1发生故障。
解决方案:当使用继承时,遵循里氏替换原则。类B继承类A时,除添加新的方法完成新增功能P2外,尽量不要重写父类A的方法,也尽量不要重载父类A的方法。
继承包含这样一层含义:父类中凡是已经实现好的方法(相对于抽象方法而言),实际上是在设定一系列的规范和契约,虽然它不强制要求所有的子类必须遵从这些契约,但是如果子类对这些非抽象方法任意修改,就会对整个继承体系造成破坏。而里氏替换原则就是表达了这一层含义。
缺点:比如使用继承会给程序带来侵入性,程序的可移植性降低,增加了对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能会产生故障。
里氏替换原则通俗的来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。它包含以下4层含义:
子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
子类中可以增加自己特有的方法。
当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。
定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象
问题由来:类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。
解决方案:将类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大降低修改类A的几率。
传递依赖关系有三种方式,接口传递、构造方法传递和setter方法传递,相信用过Spring框架的,对依赖的传递方式一定不会陌生。
在实际编程中,我们一般需要做到如下3点:
低层模块尽量都要有抽象类或接口,或者两者都有。
变量的声明类型尽量是抽象类或接口。
使用继承时遵循里氏替换原则。
依赖倒置原则的核心就是要我们面向接口编程,理解了面向接口编程,也就理解了依赖倒置。
定义:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。
问题由来:类A通过接口I依赖类B,类C通过接口I依赖类D,如果接口I对于类A和类B来说不是最小接口,则类B和类D必须去实现他们不需要的方法。
解决方案:将臃肿的接口I拆分为独立的几个接口,类A和类C分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则。
接口隔离原则的含义是:建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。也就是说,我们要为各个类建立专用的接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。在程序设计中,依赖几个专用的接口要比依赖一个综合的接口更灵活。接口是设计时对外部设定的“契约”,通过分散定义多个接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性。
采用接口隔离原则对接口进行约束时,要注意以下几点:
接口尽量小,但是要有限度。对接口进行细化可以提高程序设计灵活性是不挣的事实,但是如果过小,则会造成接口数量过多,使设计复杂化。所以一定要适度。
为依赖接口的类定制服务,只暴露给调用的类它需要的方法,它不需要的方法则隐藏起来。只有专注地为一个模块提供定制服务,才能建立最小的依赖关系。
提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情。
定义:一个对象应该对其他对象保持最少的了解。
问题由来:类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。
解决方案:尽量降低类与类之间的耦合。
迪米特法则又叫最少知道原则,最早是在1987年由美国NortheasternUniversity的Ian Holland提出。通俗的来讲,就是一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类来说,无论逻辑多么复杂,都尽量地的将逻辑封装在类的内部,对外除了提供的public方法,不对外泄漏任何信息。迪米特法则还有一个更简单的定义:只与直接的朋友通信。首先来解释一下什么是直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖、关联、组合、聚合等。其中,我们称出现成员变量、方法参数、方法返回值中的类为直接的朋友,而出现在局部变量中的类则不是直接的朋友。也就是说,陌生的类最好不要作为局部变量的形式出现在类的内部。
定义:不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。
问题由来:类T负责两个不同的职责:职责P1,职责P2。当由于职责P1需求发生改变而需要修改类T时,有可能会导致原本运行正常的职责P2功能发生故障。
解决方案:遵循单一职责原则。分别建立两个类T1、T2,使T1完成职责P1功能,T2完成职责P2功能。这样,当修改类T1时,不会使职责P2发生故障风险;同理,当修改T2时,也不会使职责P1发生故障风险。
遵循单一职责原的优点有:可以降低类的复杂度,一个类只负责一项职责,其逻辑肯定要比负责多项职责简单的多;提高类的可读性,提高系统的可维护性;变更引起的风险降低,变更是必然的,如果单一职责原则遵守的好,当修改一个功能时,可以显著降低对其他功能的影响。
1、抽象工厂模式(Abstract factory pattern): 提供一个接口, 用于创建相关或依赖对象的家族,而不需要指定具体类.
2、生成器模式(Builderpattern): 使用生成器模式封装一个产品的构造过程,并允许按步骤构造. 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示.
3、工厂模式(factorymethod pattern): 定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个. 工厂方法让类把实例化推迟到子类.
4、原型模式(prototypepattern): 当创建给定类的实例过程很昂贵或很复杂时,就使用原形模式.
5、单例模式(Singletonpattern): 确保一个类只有一个实例, 并提供全局访问点.
6、多例模式(Multitionpattern): 在一个解决方案中结合两个或多个模式,以解决一般或重复发生的问题.
1、适配器模式(Adapter pattern): 将一个类的接口, 转换成客户期望的另一个接口.适配器让原本接口不兼容的类可以合作无间. 对象适配器使用组合, 类适配器使用多重继承.
2、桥接模式(Bridgepattern): 使用桥接模式通过将实现和抽象放在两个不同的类层次中而使它们可以独立改变.
3、组合模式(compositepattern): 允许你将对象组合成树形结构来表现"整体/部分"层次结构. 组合能让客户以一致的方式处理个别对象以及对象组合.
4、装饰者模式(decoratorpattern): 动态地将责任附加到对象上, 若要扩展功能, 装饰者提供了比继承更有弹性的替代方案.
5、外观模式(facadepattern): 提供了一个统一的接口, 用来访问子系统中的一群接口. 外观定义了一个高层接口, 让子系统更容易使用.
6、亨元模式(FlyweightPattern): 如想让某个类的一个实例能用来提供许多"虚拟实例", 就使用蝇量模式.
7、代理模式(Proxypattern): 为另一个对象提供一个替身或占位符以控制对这个对象的访问.
1、责任链模式(Chain of responsibilitypattern): 通过责任链模式, 你可以为某个请求创建一个对象链. 每个对象依序检查此请求并对其进行处理或者将它传给链中的下一个对象.
2、命令模式(Commandpattern): 将"请求"封闭成对象, 以便使用不同的请求,队列或者日志来参数化其他对象. 命令模式也支持可撤销的操作.
3、解释器模式(Interpreterpattern): 使用解释器模式为语言创建解释器.
4、迭代器模式(iteratorpattern): 提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示.
5、中介者模式(Mediatorpattern) : 使用中介者模式来集中相关对象之间复杂的沟通和控制方式.
6、备忘录模式(Mementopattern): 当你需要让对象返回之前的状态时(例如, 你的用户请求"撤销"), 你使用备忘录模式.
7、观察者模式(observerpattern): 在对象之间定义一对多的依赖,这样一来, 当一个对象改变状态,依赖它的对象都会收到通知, 并自动更新.
8、状态模式(Statepattern): 允许对象在内部状态改变时改变它的行为,对象看起来好象改了它的类.
9、策略模式(strategypattern): 定义了算法族, 分别封闭起来, 让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户.
10、模板方法模式(Templatepattern): 在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中. 模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤.
11、访问者模式(visitorpattern): 当你想要为一个对象的组合增加新的能力,且封装并不重要时, 就使用访问者模式.
--------------------------------------------------------------------------------------------------
1、单一职责原则【SINGLE RESPONSIBILITY PRINCIPLE】:一个类负责一项职责.
2、里氏替换原则【LISKOVSUBSTITUTION PRINCIPLE】:继承与派生的规则.
3、依赖倒置原则【DEPENDENCEINVERSION PRINCIPLE】:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。即针对接口编程,不要针对实现编程.
4、接口隔离原则【INTERFACESEGREGATION PRINCIPLE】:建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少.
5、迪米特法则【LOW OFDEMETER】:低耦合,高内聚.
6、开闭原则【OPEN CLOSEPRINCIPLE】:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭.
7、组合/聚合复用原则【Composition/Aggregation ReusePrinciple(CARP) 】:尽量使用组合和聚合少使用继承的关系来达到复用的原则.
三种工厂模式从左到右逐步抽象,并且更具一般性。简单工厂模式可以看做工厂模式的一种简单化,可以归为工厂模式的一种我们先来看看简单工厂模式
1) 简单工厂模式
上图为简单工厂模式的类图,非常简单。FactoryBMW类就是工厂, 它负责生产宝马汽车。这个类生产的方式是这样的,通过if-else语句或者switch语句分析“客户”传递给它的参数,来new BMW523还是new BMW320.如下图代码所示:
优点:能够快速的生产某种制品。
缺点:当增加一种产品时,工厂只能修改自己的内部代码来生产这个产品,违反了开闭原则。于是,我们引出工厂方法模式。
2) 工厂方法模式
工厂方法模式通象对工厂抽象,增加了一个抽象工厂类,这个抽象工厂类能够生产某个抽象的产品(不是具体的产品)。具体的工厂实现类通过实现这个抽象工厂的方法来生产具体的不同的产品,如下图所示:
客户持有的是工厂和产品的抽象。要生产某个具体的产品,就把某个抽象工厂的具体子类传给客户即可生产。比如我们要生产ConcreProduct1,只需要调用ConcreteFactory的create()方法即可。那么这个就良好的解决了上面的问题,如果要增加产品,那么只需要增加一个具体工厂来生产这个产品就可以了。
优点:具有良好的可拓展性,复合开闭原则
缺点:对于多产品族,比如生产一系列部件来组装一个产品,工厂方法模式不能做到很好。于是,这又引出我们的下一个模式,抽象工厂模式。
3) 抽象工厂模式
我们先来看抽象工厂模式的类图
然后根据类图我们来介绍抽象工厂模式:什么是产品族呢?像图中的ProductA和ProductB就是两个产品族,他们下面有一系列的功能相似或相近的产品。还是前面的宝马车的例子。我们现在要详细的生产宝马了,于是我们假设我们的宝马车由空调和外壳构成,那么ProductA就是空调,它下面有两个品类的空调,分别是A1,A2.ProductB就是外壳产品,下面也有两种外壳。那么你很自然的就想到,两两组合肯定会有四种BMW的车啊。但是我们的工厂只有两个,Factory1负责组装ProductA1和ProduchtB1,Factory2负责组装ProductA2和ProductB2,因为其他两个可能没有市场,组装起来太不好看了。这就是抽象工厂模式,每个工厂负责组装不同的产品,而这个产品是由很多个部件构成的,每个部件又都属于不同的产品族。
那么问题来了,如果我们增加了新的产品族呢?啊哈,这个还真不好办,只能修改你的工厂了。但是如果你没有增加新的产品族,而只是在原来的产品族增加了新的产品,这个时候又增加了一个新的BMW品种了,你只需要写一个ConcreteFactory3,来组装必要的配件就可以了。
生成器模式负责将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。该模式的类图如下:
Builder:生成器接口,面向接口编程原理抽象出的接口,这个接口定义生成一个产品Product的若干操作。
ConcreteBuilder:具体的生成器实现,可以有多个,用来生成不同的产品Product。
Product:具体生成器要构造的复杂对象。
Director:这个类持有一个Builder接口声明的变量,通过初始化不同的ConcreteBuilder来创建不同的产品。
具体应用场景—导出数据的应用框架
对于导出数据的应用框架,通常在导出数据上,会有一些约束的方式,比如导出成文本格式、数据库备份形式、Excel格式、Xml格式等。
对于导出数据的应用框架,通常对于具体的导出内容和格式是有要求的,加入现在有如下要求,简单描述一下:
导出的文件,不管是什么格式,都分成3个部分,分别是文件头、文件体、文件尾。
在文件头部分,需要描述如下信息:分公司或者门市编号、导出数据的日期。
在文件体部分,需要描述如下信息:表名称,然后分条描述数据。
在文件尾部分,需要描述如下信息:输出人。
具体的看这篇博客吧,讲的比较详细了
http://blog.csdn.net/top_code/article/details/8469297
生成器模式vs工厂模式:
1、生成器模式是为了构造一个复杂的产品,而且购造这个产品遵循一定的规则(相同的过程),而抽象工厂则是为了创建成族的产品(系列产品),同族产品的构造在逻辑上并不存在必然的联系(唯一必然的联系就是大家都属于一族)。
2、生成器模式的构造方法是为了构造同一个产品,因此必须有指导者来协调进行工作,构造方法之间存在必然的业务联系,而抽象工厂的构造方法都是独立去构建自己的产品对象,因此他们不存在必然的联系。在生成器模式中客户端不直接调用构建产品部分的方法来获取最终产品,而抽象工厂中客户端是通过调用不同的工厂方法获取不同的产品。
3.在生成器模式中,那些用来构造产品不同部分的方法一般都实现为Protected形式,以防止客户端通过调用这种方法活得不可预料的结果,而抽象工厂中的这些方法必须为Public形式。否则客户无法调用来获得产品结果;
4.生成器模式的角色有生成器,产品和指导者,而抽象工厂的角色有工厂和产品。无论角色和功能怎样变换,但所含的业务逻辑角色都应该存在,这也是两个模式的业务本质。
原型模式是一种创建型模式,他通过赋值一个已经存在的实例来返回新的实例,而不是新建实例。被复制的实例就是我们所称的原型。这个原型是可定制的。
原型模式的赋值分为浅拷贝和深拷贝,顺便来复习下深拷贝和浅拷贝吧。
浅拷贝: 对值类型的成员变量进行值的复制,对引用类型的成员变量只复制引用,不复制引用的对象,当修改该副本内的引用类型的变量时,会影响原来的类。
深拷贝: 对值类型的成员变量进行值的复制,对引用类型的成员变量也进行引用对象的复制,对于副本的修改,不会影响到源对象本身。
Java中的clone()方法
(1)clone()方法将对象复制了一份并返回给调用者。一般而言,clone()方法满足下面规范:
①对任何的对象x,都有x.clone() != x;//克隆对象与原对象不是同一个对象
②对任何的对象x,都有x.clone().getClass()== x.getClass();//克隆对象与原对象的类型一样
③如果对象x的equals()方法定义恰当,那么x.clone().equals(x);应该成立。
那么我们来看看浅拷贝的实现和深拷贝的实现。Java中Object自带一个clone()方法,但是这个方法是浅拷贝的,为了实现深拷贝,必须让对应的类内对象成员变量(即这个类内部的引用类型的成员变量)实现cloneable接口,重写clone方法。
先看一下如何实现浅拷贝
//Professor没有实现Cloneable接口,默认使用java.lang.Object类的clone()方法 class Professor{ String name; int age; Professor(String name,int age){ this.name=name; this.age=age; } } //Student实现了Cloneable接口 class Student implements Cloneable{ String name;//常量对象。 int age; Professor p; Student(String name,int age,Professor p){ this.name=name; this.age=age; this.p=p; } public Object clone(){ Student o=null; try{ o=(Student)super.clone(); }catch(CloneNotSupportedException e){ System.out.println(e.toString()); } //使用Object类的clone()方法 o.p=(Professor)this.p.clone(); return o; } } public static void main(String[] args){ Professor p=new Professor("wangwu",50); Student s1=new Student("zhangsan",18,p); Student s2=(Student)s1.clone(); s2.p.name="lisi"; s2.p.age=30; //学生1的教授也变成了lisi,age为30 System.out.println("name="+s1.p.name+","+"age="+s1.p.age); } |
我们来看个例子,我把我的代码复制过来,看一下如何实现深拷贝(ArrayList也可以深拷贝)。
package prototype;
public class Teacher implements Cloneable{ String name; String id; public Teacher(String name, String id) { super(); this.name = name; this.id = id; }
@Override protected Object clone(){ // TODO Auto-generated method stub Object o=null; try{ o=super.clone(); }catch(Exception e){ e.printStackTrace(); } return o; }
} |
public class Element implements Cloneable{
String name;
public Element(String name) { super(); this.name = name; }
@Override protected Object clone(){ // TODO Auto-generated method stub Object o = null; try{ o=super.clone();
}catch(Exception e){ e.printStackTrace(); }
return o; }
public String toString(){ return name+ " "; }
} |
package prototype;
import java.util.ArrayList;
public class Student implements Cloneable{
String name; String id; Teacher tea; ArrayList
public Student(String name,String id,Teacher tea){ this.id=id; this.name=name; this.tea=tea; }
@Override protected Object clone() { // TODO Auto-generated method stub Student s = null; try{ s=(Student)super.clone(); s.tea=(Teacher)this.tea.clone(); ArrayList for(Element ele:e){ Element e = (Element) ele.clone(); newE.add(e); } s.e=newE; }catch(Exception e){
}
return s; }
public static void main(String[] args){ Student s1 = new Student("liming", "001", new Teacher("Li","100")); Element e1 = new Element("e1"); Element e2 = new Element("e2"); Element e3 = new Element("e3"); s1.e.add(e1); s1.e.add(e2); s1.e.add(e3);
Student s2 = (Student) s1.clone(); s2.tea.name="xiaohua"; for(Element ele:s2.e){ ele.name="a"; }
System.out.println(s1.tea.name+" "+s1.e); System.out.println(s2.tea.name+" "+s2.e); } } |
饿汉模式的单例是线程安全的:
静态内部类也是线程安全的
多例模式可以看做是单例模式的推广,作为对象的创建模式,多例模式或多例类有如下特点:
(1)多例类可有多个实例
(2)多例类必须自己创建、管理自己的实例,并向外界提供自己的实例。
(3)根据是否有实例上限分为:有上限多例类和无上限多例类。
多例模式就是在一个类里面一次性的创建多个实例,并将实例添加到一个list或者map中,然后获取你想要的实例就可以了。比如语言的多例模式,如下所示:
import java.util.*; class LingualResource { private String languages = "en"; private String region = "US"; private String localeCode = "en_US"; private static final String FILE_NAME = "res"; private static Map private Locale locale = null; private ResourceBundle resourceBundle = null; private LingualResource lingualResource;
private LingualResource(String language,String region) { this.localeCode = language; this.region = region; localeCode = makeLocaleCode(language,region); locale = new Locale(language,region); resourceBundle = ResourceBundle.getBundle(FILE_NAME,locale); instances.put(localeCode,this); } private LingualResource() {}
public synchronized static LingualResource getInstance(String language,String region) { if(instances.get(makeLocaleCode(language,region)) != null) { return instances.get(makeLocaleCode(language,region)); } else { return new LingualResource(language,region); } } public String getLocaleString(String code) { return resourceBundle.getString(code); } private static String makeLocaleCode(String language,String region) { return language + "_" + region; } }
class LingualResourceTest { public static void main(String[] args) { LingualResource lr = LingualResource.getInstance("en","US"); String usDollar = lr.getLocaleString("USD"); System.out.println("USD=" + usDollar); } } |
将不同地区的语言和对应的地区进行封装,创建多个实例,来实现多例模式
将一个类的接口转换成客户希望的另外一个接口,Adapter模式使得原本由于接口不兼容而不能一起工作的类可以在一起工作。适配器模式通过使用继承(实现)和组合,分为两种,分别是类适配器模式和对象适配器模式。
类适配器模式:
上图可以看出,Adaptee没有sampleOperation2方法,而客户端期望使用这个方法,于是我们使用Adapter继承Adaptee,并实现Target接口。图上没有画出来的是Client类,这个类持有一个Target类型的变量,只要将这个变量初始化为对应的Adapter,即可使用Adaptee的方法。
对象适配器方法
和前面的功能是异曲同工的,只不过继承改成了组合。Adapter持有Adaptee的对象,用这个对象来实现对应的sampleOperation1或者sampleOperation2方法。和前面相比,前面的类适配器不需要重写sampleOperation1方法,因为通过继承获得了Adaptee的对应的方法。
桥接模式的目的是把变化的部分抽象出来,使得变化部分与主类分离开来,从而将多个维度的变化彻底分离。桥接模式常常应用于多个维度变化的类(即一个类变化的原因多于1个)。
桥接模式的类图如下:
当变化的维度超过1个的时候,可以将多余1个的维度拿出来作为抽象的类。上图的Implementor就是一个多余的变化维度,将这个维度的变化的可能抽象,具体的实现通过实现这个抽象接口的方式来实现(如图中的Implementor和ConcreteImplementorA/B)。然后Abstraction也是一个变化的维度,因此它也是抽象的。这个维度持有Implementor抽象接口的一个成员变量,通过初始化不同的实际成员来实现所需的功能。Abstraction变化的维度被封装到了外面,内部由于是抽象,抽象是稳定的。如果有更多的维度,那么就让Abstraction持有更多的抽象类型对象即可。
下图介绍了一个简单的例子。汽车有小汽车和公交车,道路有高速路和城市公路,那么这就是两个变化的维度,我们让AbstractRoad作为上面类图的Abstraction,AbstractCar作为Implementor,看一下实现的过程
abstract class AbstractRoad{ AbstractCar aCar; void run(){}; } abstract class AbstractCar{ void run(){}; }
class Street extends AbstractRoad{ @Override void run() { // TODO Auto-generated method stub super.run(); aCar.run(); System.out.println("在市区街道行驶"); } } class SpeedWay extends AbstractRoad{ @Override void run() { // TODO Auto-generated method stub super.run(); aCar.run(); System.out.println("在高速公路行驶"); } } class Car extends AbstractCar{ @Override void run() { // TODO Auto-generated method stub super.run(); System.out.print("小汽车"); } } class Bus extends AbstractCar{ @Override void run() { // TODO Auto-generated method stub super.run(); System.out.print("公交车"); } }
public static void main(String[] args){
AbstractRoad speedWay = new SpeedWay(); speedWay.aCar = new Car(); speedWay.run();
AbstractRoad street = new Street(); street.aCar = new Bus(); street.run(); |
组合模式是一个很有意思的模式。最常见的可以适合组合模式的就是文件目录。比如linux的文件系统,/根目录下有若干的目录,每个目录下面又有若干的目录,到最后目录下面会有文件或者目录或者是空的文件夹。那么根目录/就是图中的Component,目录(文件夹)就是图中的composite,文件就是图中的Leaf。这样是不是就很容易理解了?这些类都实现了图中抽象类Component的方法,只不过有的方法有具体的实现代码,有的仅仅是“System.out.println(“Sorry,this object can not implement thismethod”)””。下面看个例子:
package Composite; //Component ,抽象文件类 /** * Created by Jiqing on 2016/10/5. */ abstract class AbstractFile { public abstract void add(AbstractFile file); public abstract void remove(AbstractFile file); public abstract AbstractFile getChild(int i); public abstract void killVirus(); } |
package Composite; //图片文件类,一个具体的叶子Leaf /** * Created by Jiqing on 2016/10/5. */ public class ImageFile extends AbstractFile{ private String name; public ImageFile (String name) { this.name = name; } public void add(AbstractFile file) { System.out.println("对不起,不支持该方法!"); } public void remove(AbstractFile file) { System.out.println("对不起,不支持该方法!"); } public AbstractFile getChild(int i) { System.out.println("对不起,不支持该方法!"); return null; } public void killVirus() { // 模拟杀毒 System.out.println("----对图像文件'" + name + "'进行杀毒----"); } } |
package Composite; //文本文件,一个Leaf /** * Created by Jiqing on 2016/10/5. */ public class TextFile extends AbstractFile{ private String name; public TextFile (String name) { this.name = name; } public void add(AbstractFile file) { System.out.println("对不起,不支持该方法!"); } public void remove(AbstractFile file) { System.out.println("对不起,不支持该方法!"); } public AbstractFile getChild(int i) { System.out.println("对不起,不支持该方法!"); return null; } public void killVirus() { // 模拟杀毒 System.out.println("----对文本文件'" + name + "'进行杀毒----"); } } |
package Composite;
import java.util.ArrayList; //文件夹类,一个composite /** * Created by Jiqing on 2016/10/5. */ public class Folder extends AbstractFile{ private ArrayList private String name; public Folder(String name) { this.name = name; }
public void add(AbstractFile file) { fileList.add(file); }
public void remove(AbstractFile file) { fileList.remove(file); }
public AbstractFile getChild(int i) { return (AbstractFile)fileList.get(i); // 强制转换为类型 }
public void killVirus() { System.out.println("****对文件夹'" + name + "'进行杀毒****"); // 模拟杀毒 // 递归调用成员构件的杀毒方法 for (Object obj :fileList) { ((AbstractFile)obj).killVirus(); } } } |
package Composite;
/** * Created by Jiqing on 2016/10/5. */ public class Client { public static void main(String args[]) { AbstractFile file1,file2,file3,file4,file5,folder1,folder2,folder3,folder4; folder1 = new Folder("Jim的资料"); folder2 = new Folder("图像文件"); folder3 = new Folder("文本文件"); folder4 = new Folder("视频文件");
file1 = new ImageFile("小龙女.jpg"); file2 = new ImageFile("张无忌.gif"); file3 = new TextFile("九阴真经.txt"); file4 = new TextFile("葵花宝典.doc"); file5 = new VideoFile("笑傲江湖.rmvb");
folder2.add(file1); folder2.add(file2); folder3.add(file3); folder3.add(file4); folder4.add(file5); folder1.add(folder2); folder1.add(folder3); folder1.add(folder4);
folder1.killVirus(); } } |
装饰者模式通过动态的给一个类添加一些额外的职责(就像给他添加一些装饰品一样),使用Decorator比使用子类的方式要更灵活一些。
这个类图的含义是:Component是要被装饰的抽象类,ConcreteComponent是一个具体的类。Decorator是一个抽象的装饰器类,装饰器类下面又有若干的具体的装饰器。这个的实现也比较有意思。关键是Decorator持有一个Component对象,通过这个Component对象,我们可以不断的给Component增加装饰。比如下面的例子。
package com.test.patten.decorator; //Component类 public interface Person { void doCoding(); } |
package com.test.patten.decorator;
public class Employee implements Person { @Override public void doCoding() { System.out.println("程序员加班写程序啊,写程序,终于写完了。。。"); } } |
package com.test.patten.decorator; //抽象装饰器类 public abstract class Manager implements Person{ //装饰器增加功能 public abstract void doCoding(); } |
package com.test.patten.decorator; //一个具体的装饰器 public class ManagerA extends Manager { private Person person;//给雇员升职 public ManagerA(Person person) { super(); this.person = person; } @Override public void doCoding() { doEarlyWork(); person.doCoding(); } /** * 项目经理开始前期准备工作 */ public void doEarlyWork() { System.out.println("项目经理A做需求分析"); System.out.println("项目经理A做架构设计"); System.out.println("项目经理A做详细设计"); } } |
package com.test.patten.decorator; //另一个具体的装饰器 public class ManagerB extends Manager { private Person person;//给雇员升职
public ManagerB(Person person) { super(); this.person = person; } @Override public void doCoding() { person.doCoding(); doEndWork(); } /** * 项目经理开始项目收尾工作 */ public void doEndWork() { System.out.println("项目经理B 在做收尾工作"); } } |
package com.test.patten.decorator; //main方法,多次装饰Employee,让Employee变得无比强大 public class Client { public static void main(String args[]){ Person employee = new Employee(); employee = new ManagerA(employee);//赋予程序猿项目经理A职责 employee = new ManagerB(employee);//赋予程序猿项目经理B职责 employee.doCoding(); } } //输出 项目经理A做需求分析 项目经理A做架构设计 项目经理A做详细设计 程序员加班写程序啊,写程序,终于写完了。。。 项目经理B 在做收尾工作 |
外观模式是一个比较简单的模式。它的作用是将系统内部的实现隐藏,只对外提供一系列可以访问的接口,提高安全性和可用性。
享元模式Flyweight,在拳击比赛指最轻量级。这样起名字是这样最能表达享元模式的用意。享元模式以共享的模式高效的支持大量的细粒度对象。具体来说就是:如果一个系统中存在着大量相同的对象,那么只需要共享一份对象的拷贝,而不必为每一次使用创建新的对象。享元模式一般和单例模式一起结合使用。
Flyweight:抽象享元角色,给出一个抽象接口,以规定所有的具体享元角色需要实现的方法。
ConcreteFlyweight:具体的享元对象,必须是共享的,需要封装Flyweight的内部状态。
UnsharedConcreteFlyweight:有的还有一个这个具体类,继承自Flyweight,这是非共享的享元对象。
FlyweightFactory:享元工厂,主要用来创建并管理共享的享元对象,对外提供访问享元的接口。
下面我们来看一个具体的例子。
public interface Flyweight { //一个示意性方法,参数state是外蕴状态 public void operation(String state); } public class ConcreteFlyweight implements Flyweight { private Character intrinsicState = null; /** * 构造函数,内蕴状态作为参数传入 * @param state */ public ConcreteFlyweight(Character state){ this.intrinsicState = state; } /** * 外蕴状态作为参数传入方法中,改变方法的行为, * 但是并不改变对象的内蕴状态。 */ @Override public void operation(String state) { // TODO Auto-generated method stub System.out.println("Intrinsic State = " + this.intrinsicState); System.out.println("Extrinsic State = " + state); } } |
享元工厂类,由于这个类一般只有一个,因此这里可以使用单例模式。
public class FlyweightFactory { private Map public Flyweight factory(Character state){ //先从缓存中查找对象 Flyweight fly = files.get(state); if(fly == null){ //如果对象不存在则创建一个新的Flyweight对象 fly = new ConcreteFlyweight(state); //把这个新的Flyweight对象添加到缓存中 files.put(state, fly); } return fly; } } |
客户端类
public class Client {
public static void main(String[] args) { // TODO Auto-generated method stub FlyweightFactory factory = new FlyweightFactory(); Flyweight fly = factory.factory(new Character('a')); fly.operation("First Call");
fly = factory.factory(new Character('b')); fly.operation("Second Call");
fly = factory.factory(new Character('a')); fly.operation("Third Call"); }
} |
代理模式一般是用来进行远程调用的。比如客户端需要远程调用服务器的功能,可以使用代理模式将所需要的功能让代理传递给我们的客户端,通过调用这个代理的函数,看上去就像直接调用这个服务器的功能一样。这种常见的代理模式的应用有RMI。
还有的代理呢,使用的目的是为了给被代理对象添加新的功能,增加一些新的处理。比如java 的动态代理Proxy。
动态代理Proxy
这个代理的实现由2步,第一步,让一个类实现InvocationHandler接口并实现invoke方法。第二步,使用Proxy的静态方法newProxyInstance方法创建一个实例,这个实例就是我们的代理。我们来看一个具体的例子;
public interface Subject { public void doSomething(); } public class RealSubject implements Subject { public void doSomething() { System.out.println( "call doSomething()" ); } } public class ProxyHandler implements InvocationHandler { private Object proxied;
public ProxyHandler( Object proxied ) { this.proxied = proxied; }
public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable { //在转调具体目标对象之前,可以执行一些功能处理
//转调具体目标对象的方法 return method.invoke( proxied, args); //在转调具体目标对象之后,可以执行一些功能处理 } } |
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import sun.misc.ProxyGenerator; import java.io.*; public class DynamicProxy { public static void main( String args[] ) { RealSubject real = new RealSubject(); Subject proxySubject = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(), new Class[]{Subject.class}, new ProxyHandler(real));
proxySubject.doSomething();
//write proxySubject class binary data to file createProxyClassFile(); }
public static void createProxyClassFile() { String name = "ProxySubject"; byte[] data = ProxyGenerator.generateProxyClass( name, new Class[] { Subject.class } ); try { FileOutputStream out = new FileOutputStream( name + ".class" ); out.write( data ); out.close(); } catch( Exception e ) { e.printStackTrace(); } } } |
动态代理的本质是生成一个类,这个类继承Proxy并实现Subject,从而可以进行多态开发和代理。http://www.cnblogs.com/flyoung2008/archive/2013/08/11/3251148.html
责任链模式是一种对象的行为模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。
在以下条件下可考虑使用Chain of Responsibility:
1 有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。
2 你想在不明确指定接受者的情况下,想过个对象中的一个提交一个请求。
3 可处理一个请求的对象集合应该被动态指定。
这里可以举一个简单的例子,比如项目经理报销额度不能大于500,部门经理不能大于1000,超过1000需要总经理审核。也就是说,这个可以用调用链来处理。下面来看代吗
abstract class ConsumeHandler {
private ConsumeHandler nextHandler;
public ConsumeHandler getNextHandler() { return nextHandler; }
public void setNextHandler(ConsumeHandler nextHandler) { this.nextHandler = nextHandler; }
/** user申请人 free报销费用 */ public abstract void doHandler(String user, double free);
} //项目经理 class ProjectHandler extends ConsumeHandler {
@Override public void doHandler(String user, double free) { if (free < 500) {
if (user.equals("lwx")) { System.out.println("给予报销:" + free); } else { System.out.println("报销不通过"); }
} else { if (getNextHandler() != null) {
getNextHandler().doHandler(user, free); } }
} } //部门经理 class DeptHandler extends ConsumeHandler {
@Override public void doHandler(String user, double free) { if (free < 1000) {
if (user.equals("zy")) { System.out.println("给予报销:" + free); } else { System.out.println("报销不通过"); }
} else { if (getNextHandler() != null) {
getNextHandler().doHandler(user, free); } }
} } //总经理 class GeneralHandler extends ConsumeHandler {
@Override public void doHandler(String user, double free) { if (free >=1000) {
if (user.equals("lwxzy")) { System.out.println("给予报销:" + free); } else { System.out.println("报销不通过"); }
} else { if (getNextHandler() != null) {
getNextHandler().doHandler(user, free); } }
} } |
测试下
public static void main(String[] args) {
/*ConcreteHandler handler1 = new ConcreteHandler(); ConcreteHandler handler2 = new ConcreteHandler(); handler1.setNextHandler(handler2); handler1.doHandler();*/
ProjectHandler projectHandler =new ProjectHandler(); DeptHandler deptHandler =new DeptHandler(); GeneralHandler generalHandler =new GeneralHandler(); projectHandler.setNextHandler(deptHandler); deptHandler.setNextHandler(generalHandler); projectHandler.doHandler("lwx", 450); projectHandler.doHandler("lwx", 600); projectHandler.doHandler("zy", 600); projectHandler.doHandler("zy", 1500); projectHandler.doHandler("lwxzy", 1500); } |
命令模式可以将一个请求封装为一个对象(即Command)对象,从而使你可以使用不同的请求来对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
ICommand:抽象命令,定义命令的接口
ConcreteCommand:具体命令,实现要执行的方法。他通常会持有接受者的对象,调用接收者的方法来完成命令操作。
Receiver:接收者,真正执行命令的对象。
Invoker:调用者,要求命令对象执行请求,通常会持有命令对象。可以持有很多的命令对象,是客户端调用命令的入口。
/// /// 接收者类,知道如何实施与执行一个请求相关的操作,任何类都可能作为一个接收者。 /// public class Receiver { /// /// 真正的命令实现 /// public void Action() { Console.WriteLine("Execute request!"); } }
/// /// 抽象命令类,用来声明执行操作的接口 /// public interface ICommand { void Execute(); }
/// /// 具体命令类,实现具体命令。 /// public class ConcereteCommand : ICommand { // 具体命令类包含有一个接收者,将这个接收者对象绑定于一个动作 private Receiver receiver;
public ConcereteCommand(Receiver receiver) { this.receiver = receiver; }
/// /// 说这个实现是“虚”的,因为它是通过调用接收者相应的操作来实现Execute的 /// public void Execute() { receiver.Action(); } }
/// /// 调度类,要求该命令执行这个请求 /// public class Invoker { private ICommand command;
/// /// 设置命令 /// /// public void SetCommand(ICommand command) { this.command = command; }
/// /// 执行命令 /// public void ExecuteCommand() { command.Execute(); } } |
客户端代码
class Program { static void Main(string[] args) { Receiver receiver = new Receiver(); ICommand command = new ConcereteCommand(receiver); Invoker invoker = new Invoker();
invoker.SetCommand(command); invoker.ExecuteCommand();
Console.Read(); } } |
下面来看看如何使用命令模式来实现redo和undo操作。可以通过增加一个命令管理类,所有命令的执行都通过命令管理类来执行。让所有的执行过的命令都存储在这个管理类的栈里面。
抽象命令接口
package com.xueyoucto.xueyou;
/** * Created by Administrator on 2016-07-05. */ public interface Command { public void execute(); public void undo(); } |
具体的实现类CutCommand
package com.xueyoucto.xueyou;
/** * Created by Administrator on 2016-07-05. */ public class CutCommand implements Command { private String name;
public CutCommand(String name) { this.name = name; }
@Override public void undo() { System.out.println("command:" + name + " is undo"); }
@Override public void execute() { System.out.println("command:" + name + " is execute"); } } |
具体的InsertCommand类
package com.xueyoucto.xueyou;
/** * Created by Administrator on 2016-07-05. */ public class insertCommand implements Command{ private String cmdName; private String id; private String name; private int age;
public insertCommand(String cmdName, String id, String name, int age) { this.cmdName = cmdName; this.id = id; this.name = name; this.age = age; }
@Override public void undo() { System.out.println(cmdName + " undo"); System.out.println("执行delete * from table1 where id = " +id ); }
@Override public void execute() { System.out.println(cmdName + " execute"); System.out.println("执行insert into table1(id,name,age) values(" + id + "," + name + "," + age + ")"); } } |
命令管理类
package com.xueyoucto.xueyou;
import java.util.Stack;
/** * Created by Administrator on 2016-07-05. */ public class CommandManager { private Stack private Stack
public void executeCommand(Command command){ command.execute(); undoCommand.push(command);
if(!redoCommand.empty()){ redoCommand.clear(); } } public void undo(){ if(!undoCommand.empty()){ Command command = undoCommand.pop(); command.undo(); redoCommand.push(command); } } public void redo(){ if(!redoCommand.isEmpty()){ Command command = redoCommand.pop(); command.execute(); } } } |
客户端类
package com.xueyoucto.xueyou;
/** * Hello world! */ public class App { public static void main(String[] args) { System.out.println("Hello World!"); CommandManager commandManager = new CommandManager(); commandManager.executeCommand(new CutCommand("cut")); commandManager.executeCommand(new insertCommand("insert","0001","小刚",12)); commandManager.undo(); commandManager.undo(); commandManager.redo(); commandManager.redo();
} } |
解释器模式面对的是复杂的预算模型。通常解释器是一个模型公式,通过输入参数获得你所需要的结果。
解释器模式在实际使用中较少,了解即可。
迭代器模式是平常用的最多的模式,也是我平常最会用的模式,就先不写了吧。把类图放在这里吧。
用一个中介者对象封装一系列的对象交互,中介者使个对象不需要显示的相互作用,从而是耦合松散,而且可以独立的改变他们之间的交互。
终结者模式的结构:
我们来看一个简单的例子,有两个类A和B,类中各有一个数字,并且要保证类B中的数字永远是类A中数字的100倍。也就是说,当修改类A的数时,将这个数字乘以100赋给类B,而修改类B时,要将数除以100赋给类A。类A类B互相影响,就称为同事类。代码如下:
abstract class AbstractColleague { protected int number; public int getNumber() { return number; } public void setNumber(int number){ this.number = number; } //抽象方法,修改数字时同时修改关联对象 public abstract void setNumber(int number, AbstractColleague coll); } class ColleagueA extends AbstractColleague{ public void setNumber(int number, AbstractColleague coll) { this.number = number; coll.setNumber(number*100); } }
class ColleagueB extends AbstractColleague{ public void setNumber(int number, AbstractColleague coll) { this.number = number; coll.setNumber(number/100); } } public class Client { public static void main(String[] args){ AbstractColleague collA = new ColleagueA(); AbstractColleague collB = new ColleagueB(); System.out.println("==========设置A影响B=========="); collA.setNumber(1288, collB); System.out.println("collA的number值:"+collA.getNumber()); System.out.println("collB的number值:"+collB.getNumber()); System.out.println("==========设置B影响A=========="); collB.setNumber(87635, collA); System.out.println("collB的number值:"+collB.getNumber()); System.out.println("collA的number值:"+collA.getNumber()); } } |
有了中介者之后,A和B就解耦了,同时你再增加新的同事也可以满足这种模式了。同时当有多个同事的时候,比如A能影响CDEFG若干同事,现在只需要交给中介者模式去做就可以了。
abstract class AbstractColleague { protected int number;
public int getNumber() { return number; }
public void setNumber(int number){ this.number = number; } //注意这里的参数不再是同事类,而是一个中介者 public abstract void setNumber(int number, AbstractMediator am); }
class ColleagueA extends AbstractColleague{
public void setNumber(int number, AbstractMediator am) { this.number = number; am.AaffectB(); } }
class ColleagueB extends AbstractColleague{
@Override public void setNumber(int number, AbstractMediator am) { this.number = number; am.BaffectA(); } }
abstract class AbstractMediator { protected AbstractColleague A; protected AbstractColleague B;
public AbstractMediator(AbstractColleague a, AbstractColleague b) { A = a; B = b; }
public abstract void AaffectB();
public abstract void BaffectA();
} class Mediator extends AbstractMediator {
public Mediator(AbstractColleague a, AbstractColleague b) { super(a, b); }
//处理A对B的影响 public void AaffectB() { int number = A.getNumber(); B.setNumber(number*100); }
//处理B对A的影响 public void BaffectA() { int number = B.getNumber(); A.setNumber(number/100); } }
public class Client { public static void main(String[] args){ AbstractColleague collA = new ColleagueA(); AbstractColleague collB = new ColleagueB();
AbstractMediator am = new Mediator(collA, collB);
System.out.println("==========通过设置A影响B=========="); collA.setNumber(1000, am); System.out.println("collA的number值为:"+collA.getNumber()); System.out.println("collB的number值为A的10倍:"+collB.getNumber());
System.out.println("==========通过设置B影响A=========="); collB.setNumber(1000, am); System.out.println("collB的number值为:"+collB.getNumber()); System.out.println("collA的number值为B的0.1倍:"+collA.getNumber());
} } |
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样就可以将该对象恢复到原先保存的状态。
备忘录模式可以实现恢复原来的状态,这是通过将这个状态保存到备忘录中实现的。类图的结构的含义是:
发起人:记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和恢复备忘录。
备忘录:负责存储发起人对象的内部状态,在需要的时候提供发起人的内部状态。
管理角色:对备忘录进行管理,保存和提供备忘录
class Originator { private String state = "";
public String getState() { return state; } public void setState(String state) { this.state = state; } public Memento createMemento(){ return new Memento(this.state); } public void restoreMemento(Memento memento){ this.setState(memento.getState()); } }
class Memento { private String state = ""; public Memento(String state){ this.state = state; } public String getState() { return state; } public void setState(String state) { this.state = state; } } class Caretaker { private Memento memento; public Memento getMemento(){ return memento; } public void setMemento(Memento memento){ this.memento = memento; } } public class Client { public static void main(String[] args){ Originator originator = new Originator(); originator.setState("状态1"); System.out.println("初始状态:"+originator.getState()); Caretaker caretaker = new Caretaker(); caretaker.setMemento(originator.createMemento()); originator.setState("状态2"); System.out.println("改变后状态:"+originator.getState()); originator.restoreMemento(caretaker.getMemento()); System.out.println("恢复后状态:"+originator.getState()); } } |
看一个多状态多备忘录的模式
class Originator { private String state1 = ""; private String state2 = ""; private String state3 = "";
public String getState1() { return state1; } public void setState1(String state1) { this.state1 = state1; } public String getState2() { return state2; } public void setState2(String state2) { this.state2 = state2; } public String getState3() { return state3; } public void setState3(String state3) { this.state3 = state3; } public Memento createMemento(){ return new Memento(BeanUtils.backupProp(this)); }
public void restoreMemento(Memento memento){ BeanUtils.restoreProp(this, memento.getStateMap()); } public String toString(){ return "state1="+state1+"state2="+state2+"state3="+state3; } } class Memento { private Map
public Memento(Map this.stateMap = map; }
public Map return stateMap; }
public void setStateMap(Map this.stateMap = stateMap; } } class BeanUtils { public static Map Map try{ BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass()); PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors(); for(PropertyDescriptor des: descriptors){ String fieldName = des.getName(); Method getter = des.getReadMethod(); Object fieldValue = getter.invoke(bean, new Object[]{}); if(!fieldName.equalsIgnoreCase("class")){ result.put(fieldName, fieldValue); } }
}catch(Exception e){ e.printStackTrace(); } return result; }
public static void restoreProp(Object bean, Map try { BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass()); PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors(); for(PropertyDescriptor des: descriptors){ String fieldName = des.getName(); if(propMap.containsKey(fieldName)){ Method setter = des.getWriteMethod(); setter.invoke(bean, new Object[]{propMap.get(fieldName)}); } } } catch (Exception e) { e.printStackTrace(); } } } class Caretaker { private Map public Memento getMemento(String index){ return memMap.get(index); }
public void setMemento(String index, Memento memento){ this.memMap.put(index, memento); } } class Client { public static void main(String[] args){ Originator ori = new Originator(); Caretaker caretaker = new Caretaker(); ori.setState1("中国"); ori.setState2("强盛"); ori.setState3("繁荣"); System.out.println("===初始化状态===\n"+ori);
caretaker.setMemento("001",ori.createMemento()); ori.setState1("软件"); ori.setState2("架构"); ori.setState3("优秀"); System.out.println("===修改后状态===\n"+ori);
ori.restoreMemento(caretaker.getMemento("001")); System.out.println("===恢复后状态===\n"+ori); } } |
我们先来看观察者模式,这个模式啊,说实话,以前考试的时候考过,我还特意留意过了,当时也学会了,但是时间久了就忘了。
观察者模式有什么用呢?它的用处之一就是通知,怎么通知呢?Subject有个Notify方法,这个方法负责通知所有的观察者。这个模式有个好处,就是一旦被观察者发生了改变,那么就可以通知观察者你也要更新数据了,特别适合气象局,真的。
先来简单的介绍观察者模式的类图结构:
Subject:一个抽象类或者接口,面向接口编程嘛,这是必须的。他就是被观察者。
ConcreteSubject:具体的被观察者的实现
Observer:观察者接口
ConcreteObserver:具体的观察者实现。
观察者模式的基本运行规则是这样的:
1.一个观察者对某个东西感兴趣了,于是它向上帝(客户端)请求,你把我绑到这上面吧,我要时时刻刻的了解它的变化(Subject的attach()方法);
2.这个东西发生了更新,于是通知所有的绑到它上面的观察者(Subject的notify方法)
3.观察者接收到notify方法之后,更新自己的数据(Observer的update方法)
4.某个观察者厌倦了这种一夫多妻制,他想修了自己的老公了,于是他请求上帝离婚(Detach)。
Ok,我们来看看代码实现吧
/// /// 抽象主题类 /// public abstract class Subject { private IList
/// /// 增加观察者 /// /// public void Attach(Observer observer) { observers.Add(observer); }
/// /// 移除观察者 /// /// public void Detach(Observer observer) { observers.Remove(observer); }
/// /// 向观察者(们)发出通知 /// public void Notify() { foreach (Observer o in observers) { o.Update(); } } }
/// /// 抽象观察者类,为所有具体观察者定义一个接口,在得到通知时更新自己 /// public abstract class Observer { public abstract void Update(); }
/// /// 具体观察者或具体通知者,将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个具体子类实现。 /// public class ConcreteSubject : Subject { private string subjectState;
/// /// 具体观察者的状态 /// public string SubjectState { get { return subjectState; } set { subjectState = value; } } }
/// /// 具体观察者,实现抽象观察者角色所要求的更新接口,已是本身状态与主题状态相协调 /// public class ConcreteObserver : Observer { private string observerState; private string name; private ConcreteSubject subject;
/// /// 具体观察者用一个具体主题来实现 /// public ConcreteSubject Subject { get { return subject; } set { subject = value; } }
public ConcreteObserver(ConcreteSubject subject, string name) { this.subject = subject; this.name = name; }
/// /// 实现抽象观察者中的更新操作 /// public override void Update() { observerState = subject.SubjectState; Console.WriteLine("The observer's state of {0} is {1}", name, observerState); } } |
class Program { static void Main(string[] args) { // 具体主题角色通常用具体自来来实现 ConcreteSubject subject = new ConcreteSubject();
subject.Attach(new ConcreteObserver(subject, "Observer A")); subject.Attach(new ConcreteObserver(subject, "Observer B")); subject.Attach(new ConcreteObserver(subject, "Observer C"));
subject.SubjectState = "Ready"; subject.Notify();
Console.Read(); } } |
状态模式和策略模式超级像,类图都几乎一样,那么怎么区别呢?如果描述的是状态的转换,就叫状态模式,如果更偏向于不同的行为策略,那就是策略模式啦。
封装某些作用域某种数据结构中各元素的操作,它可以在不改变数据结构的千条线定义作用于这些元素的新的方式。
先来说说类图结构
看到这里是不是还是不懂访问者模式到底干啥?下面的代码实现就讲了访问者模式干了啥,嫌长?没关系,我来解释一下。就是Visitor想要访问Element,这是前提。于是Visitor的visit()方法调用了Element的一个doSomething()方法。但是不是直接调用的,而是通过傻呢?是通过Element的accept(Visitor v)调用的。总结一下就是先实例化Element,然后实例化Visitor,然后调用Element的方法accept(Visitor v),参数是前面实例化的Visitor。然后这个accept方法会调用Visitor的visit(Element e)方法,visit方法又会调用这个Element的doSomthing 方法。真绕!!!!
代码实现
abstract class Element { public abstract void accept(IVisitor visitor); public abstract void doSomething(); }
interface IVisitor { public void visit(ConcreteElement1 el1); public void visit(ConcreteElement2 el2); }
class ConcreteElement1 extends Element { public void doSomething(){ System.out.println("这是元素1"); }
public void accept(IVisitor visitor) { visitor.visit(this); } }
class ConcreteElement2 extends Element { public void doSomething(){ System.out.println("这是元素2"); }
public void accept(IVisitor visitor) { visitor.visit(this); } } class Visitor implements IVisitor {
public void visit(ConcreteElement1 el1) { el1.doSomething(); }
public void visit(ConcreteElement2 el2) { el2.doSomething(); } }
class ObjectStruture { public static List List Random ran = new Random(); for(int i=0; i<10; i++){ int a = ran.nextInt(100); if(a>50){ list.add(new ConcreteElement1()); }else{ list.add(new ConcreteElement2()); } } return list; } }
public class Client { public static void main(String[] args){ List for(Element e: list){ e.accept(new Visitor()); } } } |
用来把一个接口转化成另一个接口。
这个模式将抽象和抽象操作的实现进行了解耦,这样使得抽象和实现可以独立地变化。
使得客户端看来单个对象和对象的组合是同等的。换句话说,某个类型的方法同时也接受自身类型作为参数。
动态的给一个对象附加额外的功能,这也是子类的一种替代方式。可以看到,在创建一个类型的时候,同时也传入同一类型的对象。这在JDK里随处可见,你会发现它无处不在,所以下面这个列表只是一小部分。
给一组组件,接口,抽象,或者子系统提供一个简单的接口。
使用缓存来加速大量小对象的访问时间。
代理模式是用一个简单的对象来代替一个复杂的或者创建耗时的对象。
创建模式
抽象工厂模式提供了一个协议来生成一系列的相关或者独立的对象,而不用指定具体对象的类型。它使得应用程序能够和使用的框架的具体实现进行解耦。这在JDK或者许多框架比如Spring中都随处可见。它们也很容易识别,一个创建新对象的方法,返回的却是接口或者抽象类的,就是抽象工厂模式了。
定义了一个新的类来构建另一个类的实例,以简化复杂对象的创建。建造模式通常也使用方法链接来实现。
就是一个返回具体对象的方法。
使得类的实例能够生成自身的拷贝。如果创建一个对象的实例非常复杂且耗时时,就可以使用这种模式,而不重新创建一个新的实例,你可以拷贝一个对象并直接修改它。
用来确保类只有一个实例。Joshua Bloch在Effetive Java中建议到,还有一种方法就是使用枚举。
行为模式
通过把请求从一个对象传递到链条中下一个对象的方式,直到请求被处理完毕,以实现对象间的解耦。
将操作封装到对象内,以便存储,传递和返回。
这个模式通常定义了一个语言的语法,然后解析相应语法的语句。
提供一个一致的方法来顺序访问集合中的对象,这个方法与底层的集合的具体实现无关。
通过使用一个中间对象来进行消息分发以及减少类之间的直接依赖。
生成对象状态的一个快照,以便对象可以恢复原始状态而不用暴露自身的内容。Date对象通过自身内部的一个long值来实现备忘录模式。
这个模式通过一个无意义的对象来代替没有对象这个状态。它使得你不用额外对空对象进行处理。
它使得一个对象可以灵活的将消息发送给感兴趣的对象。
通过改变对象内部的状态,使得你可以在运行时动态改变一个对象的行为。
使用这个模式来将一组算法封装成一系列对象。通过传递这些对象可以灵活的改变程序的功能。
让子类可以重写方法的一部分,而不是整个重写,你可以控制子类需要重写那些操作。
提供一个方便的可维护的方式来操作一组对象。它使得你在不改变操作的对象前提下,可以修改或者扩展对象的行为。