Java设计模式超详细
Posted 软件猫
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java设计模式超详细相关的知识,希望对你有一定的参考价值。
正好研究生开了这门课,叫做高级软件设计。本人虽然了解c++,但是不熟,老师上课讲的很深,java的设计模式比较熟,所以听得很懂。同时呢,老师上课还讲了C++的一些经典设计模式的实现(好吧,实际上是大部分),但是我这个时候基本神游天外了。幸运的是,考试只考了java版本的,哈哈。然后考前整理了下知识,发表到博客上,供大家参考
六大设计原则
1、开闭原则(Open Close Principle)
定义:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。
问题由来:在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误,也可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试。
解决方案:当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
2、里氏代换原则(Liskov SubstitutionPrinciple)
定义一:如果对每一个类型为 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层含义:
子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
子类中可以增加自己特有的方法。
当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。
3、依赖导致原则
定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象
问题由来:类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。
解决方案:将类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大降低修改类A的几率。
传递依赖关系有三种方式,接口传递、构造方法传递和setter方法传递,相信用过Spring框架的,对依赖的传递方式一定不会陌生。
在实际编程中,我们一般需要做到如下3点:
低层模块尽量都要有抽象类或接口,或者两者都有。
变量的声明类型尽量是抽象类或接口。
使用继承时遵循里氏替换原则。
依赖倒置原则的核心就是要我们面向接口编程,理解了面向接口编程,也就理解了依赖倒置。
4、接口隔离原则(Interface SegregationPrinciple)
定义:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。
问题由来:类A通过接口I依赖类B,类C通过接口I依赖类D,如果接口I对于类A和类B来说不是最小接口,则类B和类D必须去实现他们不需要的方法。
解决方案:将臃肿的接口I拆分为独立的几个接口,类A和类C分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则。
接口隔离原则的含义是:建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。也就是说,我们要为各个类建立专用的接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。在程序设计中,依赖几个专用的接口要比依赖一个综合的接口更灵活。接口是设计时对外部设定的“契约”,通过分散定义多个接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性。
采用接口隔离原则对接口进行约束时,要注意以下几点:
接口尽量小,但是要有限度。对接口进行细化可以提高程序设计灵活性是不挣的事实,但是如果过小,则会造成接口数量过多,使设计复杂化。所以一定要适度。
为依赖接口的类定制服务,只暴露给调用的类它需要的方法,它不需要的方法则隐藏起来。只有专注地为一个模块提供定制服务,才能建立最小的依赖关系。
提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情。
5、迪米特法则(最少知道原则)(Demeter Principle)
定义:一个对象应该对其他对象保持最少的了解。
问题由来:类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。
解决方案:尽量降低类与类之间的耦合。
迪米特法则又叫最少知道原则,最早是在1987年由美国NortheasternUniversity的Ian Holland提出。通俗的来讲,就是一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类来说,无论逻辑多么复杂,都尽量地的将逻辑封装在类的内部,对外除了提供的public方法,不对外泄漏任何信息。迪米特法则还有一个更简单的定义:只与直接的朋友通信。首先来解释一下什么是直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖、关联、组合、聚合等。其中,我们称出现成员变量、方法参数、方法返回值中的类为直接的朋友,而出现在局部变量中的类则不是直接的朋友。也就是说,陌生的类最好不要作为局部变量的形式出现在类的内部。
6、单一职责原则
定义:不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。
问题由来:类T负责两个不同的职责:职责P1,职责P2。当由于职责P1需求发生改变而需要修改类T时,有可能会导致原本运行正常的职责P2功能发生故障。
解决方案:遵循单一职责原则。分别建立两个类T1、T2,使T1完成职责P1功能,T2完成职责P2功能。这样,当修改类T1时,不会使职责P2发生故障风险;同理,当修改T2时,也不会使职责P1发生故障风险。
遵循单一职责原的优点有:可以降低类的复杂度,一个类只负责一项职责,其逻辑肯定要比负责多项职责简单的多;提高类的可读性,提高系统的可维护性;变更引起的风险降低,变更是必然的,如果单一职责原则遵守的好,当修改一个功能时,可以显著降低对其他功能的影响。
设计模式概述-24种设计模式和七大设计原则
一、创建型模式
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) 】:尽量使用组合和聚合少使用继承的关系来达到复用的原则.
24种设计模式详解
创建型模式
1简单工厂模式、工厂模式和抽象工厂模式
三种工厂模式从左到右逐步抽象,并且更具一般性。简单工厂模式可以看做工厂模式的一种简单化,可以归为工厂模式的一种我们先来看看简单工厂模式
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 pattern)
生成器模式负责将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。该模式的类图如下:
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.生成器模式的角色有生成器,产品和指导者,而抽象工厂的角色有工厂和产品。无论角色和功能怎样变换,但所含的业务逻辑角色都应该存在,这也是两个模式的业务本质。
原型模式(prototype pattern)
原型模式是一种创建型模式,他通过赋值一个已经存在的实例来返回新的实例,而不是新建实例。被复制的实例就是我们所称的原型。这个原型是可定制的。
原型模式的赋值分为浅拷贝和深拷贝,顺便来复习下深拷贝和浅拷贝吧。
浅拷贝: 对值类型的成员变量进行值的复制,对引用类型的成员变量只复制引用,不复制引用的对象,当修改该副本内的引用类型的变量时,会影响原来的类。
深拷贝: 对值类型的成员变量进行值的复制,对引用类型的成员变量也进行引用对象的复制,对于副本的修改,不会影响到源对象本身。
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<Element> e = new 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<Element> newE = new 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);
|
单例模式(Singleton pattern)
饿汉模式的单例是线程安全的:
静态内部类也是线程安全的
多例模式(Multition pattern)
多例模式可以看做是单例模式的推广,作为对象的创建模式,多例模式或多例类有如下特点:
(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<String,LingualResource> instances = new HashMap< String,LingualResource >(); 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 pattern)
将一个类的接口转换成客户希望的另外一个接口,Adapter模式使得原本由于接口不兼容而不能一起工作的类可以在一起工作。适配器模式通过使用继承(实现)和组合,分为两种,分别是类适配器模式和对象适配器模式。
类适配器模式:
上图可以看出,Adaptee没有sampleOperation2方法,而客户端期望使用这个方法,于是我们使用Adapter继承Adaptee,并实现Target接口。图上没有画出来的是Client类,这个类持有一个Target类型的变量,只要将这个变量初始化为对应的Adapter,即可使用Adaptee的方法。
对象适配器方法
和前面的功能是异曲同工的,只不过继承改成了组合。Adapter持有Adaptee的对象,用这个对象来实现对应的sampleOperation1或者sampleOperation2方法。和前面相比,前面的类适配器不需要重写sampleOperation1方法,因为通过继承获得了Adaptee的对应的方法。
桥接模式(Bridge pattern)
桥接模式的目的是把变化的部分抽象出来,使得变化部分与主类分离开来,从而将多个维度的变化彻底分离。桥接模式常常应用于多个维度变化的类(即一个类变化的原因多于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(); |
组合模式(composite pattern)
组合模式是一个很有意思的模式。最常见的可以适合组合模式的就是文件目录。比如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<AbstractFile> fileList = new ArrayList<AbstractFile>(); // 规定集合中成员类型 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 pattern)
装饰者模式通过动态的给一个类添加一些额外的职责(就像给他添加一些装饰品一样),使用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 在做收尾工作 |
外观模式(facade pattern)
外观模式是一个比较简单的模式。它的作用是将系统内部的实现隐藏,只对外提供一系列可以访问的接口,提高安全性和可用性。
亨元模式(Flyweight Pattern)
享元模式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<Character,Flyweight> files = new HashMap<Character,Flyweight>(); 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");
|
代理模式(Proxy pattern)
代理模式一般是用来进行远程调用的。比如客户端需要远程调用服务器的功能,可以使用代理模式将所需要的功能让代理传递给我们的客户端,通过调用这个代理的函数,看上去就像直接调用这个服务器的功能一样。这种常见的代理模式的应用有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 pattern)
责任链模式是一种对象的行为模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。
在以下条件下可考虑使用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 pattern)
命令模式可以将一个请求封装为一个对象(即Command)对象,从而使你可以使用不同的请求来对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
ICommand:抽象命令,定义命令的接口
ConcreteCommand:
以上是关于Java设计模式超详细的主要内容,如果未能解决你的问题,请参考以下文章