创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
构造型模式,共七种:适配器模式、装饰器模式、代理模式、外不雅观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、不雅观察者模式、迭代子模式、任务链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、阐明器模式。

一、创建模式(5种)
工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
1 工厂模式1.1 大略工厂模式
定义:定义了一个创建工具的类,由这个类来封装实例化工具的行为。
举例:(我们举一个pizza工厂的例子)
pizza工厂一共生产三种类型的pizza:chesse,pepper,greak。通过工厂类(SimplePizzaFactory)实例化这三种类型的工具。类图如下:
工厂类的代码:
public class SimplePizzaFactory { public Pizza CreatePizza(String ordertype) { Pizza pizza = null; if (ordertype.equals("cheese")) { pizza = new CheesePizza(); } else if (ordertype.equals("greek")) { pizza = new GreekPizza(); } else if (ordertype.equals("pepper")) { pizza = new PepperPizza(); } return pizza; }}
大略工厂存在的问题与办理方法: 大略工厂模式有一个问题便是,类的创建依赖工厂类,也便是说,如果想要拓展程序,必须对工厂类进行修正,这违背了开闭原则,以是,从设计角度考虑,有一定的问题,如何办理?我们可以定义一个创建工具的抽象方法并创建多个不同的工厂类实现该抽象方法,这样一旦须要增加新的功能,直接增加新的工厂类就可以了,不须要修正之前的代码。这种方法也便是我们接下来要说的工厂方法模式。
1.2 工厂方法模式
定义:定义了一个创建工具的抽象方法,由子类决定要实例化的类。工厂方法模式将工具的实例化推迟到子类。
举例:(我们依然举pizza工厂的例子,不过这个例子中,pizza产地有两个:伦敦和纽约)。添加了一个新的产地,如果用大略工厂模式的的话,我们要去修正工厂代码,并且会增加一堆的if else语句。而工厂方法模式战胜了大略工厂要修正代码的缺陷,它会直接创建两个工厂,纽约工厂和伦敦工厂。类图如下:
OrderPizza中有个抽象的方法:
abstract Pizza createPizza();
两个工厂类继续OrderPizza并实现抽象方法:
public class LDOrderPizza extends OrderPizza { Pizza createPizza(String ordertype) { Pizza pizza = null; if (ordertype.equals("cheese")) { pizza = new LDCheesePizza(); } else if (ordertype.equals("pepper")) { pizza = new LDPepperPizza(); } return pizza; }}public class NYOrderPizza extends OrderPizza { Pizza createPizza(String ordertype) {Pizza pizza = null; if (ordertype.equals("cheese")) {pizza = new NYCheesePizza();} else if (ordertype.equals("pepper")) {pizza = new NYPepperPizza();}return pizza; } }
通过不同的工厂会得到不同的实例化的工具,PizzaStroe的代码如下:
public class PizzaStroe { public static void main(String[] args) { OrderPizza mOrderPizza; mOrderPizza = new NYOrderPizza(); }}
实在这个模式的好处便是,如果你现在想增加一个功能,只需做一个实现类就OK了,无需去改动现成的代码。这样做,拓展性较好!
工厂方法存在的问题与办理方法:客户端须要创建类的详细的实例。大略来说便是用户要订纽约工厂的披萨,他必须去纽约工厂,想订伦敦工厂的披萨,必须去伦敦工厂。 当伦敦工厂和纽约工厂发生变革了,用户也要随着变革,这无疑就增加了用户的操作繁芜性。为理解决这一问题,我们可以把工厂类抽象为接口,用户只须要去找默认的工厂提出自己的需求(传入参数),便能得到自己想要产品,而不用根据产品去探求不同的工厂,方便用户操作。这也便是我们接下来要说的抽象工厂模式。
1.3 抽象工厂模式
定义:定义了一个接口用于创建干系或有依赖关系的工具族,而无需明确指定详细类。
举例:(我们依然举pizza工厂的例子,pizza工厂有两个:纽约工厂和伦敦工厂)。类图如下:
工厂的接口:
public interface AbsFactory { Pizza CreatePizza(String ordertype) ;}
工厂的实现:
public class LDFactory implements AbsFactory { @Override public Pizza CreatePizza(String ordertype) { Pizza pizza = null; if ("cheese".equals(ordertype)) { pizza = new LDCheesePizza(); } else if ("pepper".equals(ordertype)) { pizza = new LDPepperPizza(); } return pizza; }}
PizzaStroe的代码如下:
public class PizzaStroe { public static void main(String[] args) { OrderPizza mOrderPizza; mOrderPizza = new OrderPizza("London"); }}
办理了工厂方法模式的问题:在抽象工厂中PizzaStroe中只须要传入参数就可以实例化工具。
1.4 工厂模式适用的场合
大量的产品须要创建,并且这些产品具有共同的接口 。
1.5 三种工厂模式的利用选择
大略工厂 : 用光降盆同一等级构造中的任意产品。(不支持拓展增加产品)
工厂方法 :用光降盆同一等级构造中的固定产品。(支持拓展增加产品)
抽象工厂 :用光降盆不同产品族的全部产品。(支持拓展增加产品;支持增加产品族)
大略工厂的适用场合:只有伦敦工厂(只有这一个等级),并且这个工厂只生产三种类型的pizza:chesse,pepper,greak(固定产品)。
工厂方法的适用场合:现在不只有伦敦工厂,还增设了纽约工厂(仍旧是同一等级构造,但是支持了产品的拓展),这两个工厂依然只生产三种类型的pizza:chesse,pepper,greak(固定产品)。
抽象工厂的适用场合:不只增设了纽约工厂(仍旧是同一等级构造,但是支持了产品的拓展),这两个工厂还增加了一种新的类型的pizza:chinese pizza(增加产品族)。
以是说抽象工厂就像工厂,而工厂方法则像是工厂的一种产品生产线。因此,我们可以用抽象工厂模式创建工厂,而用工厂方法模式创建生产线。比如,我们可以利用抽象工厂模式创建伦敦工厂和纽约工厂,利用工厂方法实现cheese pizza和greak pizza的生产。类图如下:
总结一下三种模式:
大略工厂模式便是建立一个实例化工具的类,在该类中对多个工具实例化。工厂方法模式是定义了一个创建工具的抽象方法,由子类决定要实例化的类。这样做的好处是再有新的类型的工具须要实例化只要增加子类即可。抽象工厂模式定义了一个接口用于创建工具族,而无需明确指定详细类。抽象工厂也是把工具的实例化交给了子类,即支持拓展。同时供应给客户端接口,避免了用户直接操作子类工厂。
2 单例模式定义:确保一个类最多只有一个实例,并供应一个全局访问点
单例模式可以分为两种:预加载和
2.1 预加载
顾名思义,便是预先加载。再进一步阐明便是还没有利用该单例工具,但是,该单例工具就已经被加载到内存了。
public class PreloadSingleton { public static PreloadSingleton instance = new PreloadSingleton(); //其他的类无法实例化单例类的工具 private PreloadSingleton() { }; public static PreloadSingleton getInstance() { return instance; }}
很明显,没有利用该单例工具,该工具就被加载到了内存,会造成内存的摧残浪费蹂躏。
2.2
为了避免内存的摧残浪费蹂躏,我们可以采取
public class Singleton { private static Singleton instance=null; private Singleton(){ }; public static Singleton getInstance() { if(instance==null) { instance=new Singleton(); } return instance; }}
2.3 单例模式和线程安全
(1)预加载只有一条语句return instance,这显然可以担保线程安全。但是,我们知道预加载会造成内存的摧残浪费蹂躏。
(2)
不知足原子性或者顺序性,线程肯定是不屈安的,这是基本的知识,不再赘述。我紧张讲一下为什么new Singleton()无法担保顺序性。我们知道创建一个工具分三步:
memory=allocate();//1:初始化内存空间 ctorInstance(memory);//2:初始化工具 instance=memory();//3:设置instance指向刚分配的内存地址
jvm为了提高程序实行性能,会对没有依赖关系的代码进行重排序,上面2和3行代码可能被重新排序。我们用两个线程来解释线程是不屈安的。线程A和线程B都创建工具。个中,A2和A3的重排序,将导致线程B在B1处判断出instance不为空,线程B接下来将访问instance引用的工具。此时,线程B将会访问到一个还未初始化的工具(线程不屈安)。
2.4 担保
我们首先想到的便是利用synchronized关键字。synchronized加载getInstace()函数上确实担保了线程的安全。但是,如果要常常的调用getInstance()方法,不管有没有初始化实例,都会唤醒和壅塞线程。为了避免线程的高下文切换花费大量韶光,如果工具已经实例化了,我们没有必要再利用synchronized加锁,直接返回工具。
public class Singleton { private static Singleton instance = null; private Singleton() { }; public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }}
我们把sychronized加在if(instance==null)判断语句里面,担保instance未实例化的时候才加锁
public class Singleton { private static Singleton instance = null; private Singleton() { }; public static synchronized Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; }}
我们经由2.3的谈论知道new一个工具的代码是无法担保顺序性的,因此,我们须要利用另一个关键字volatile担保工具实例化过程的顺序性。
public class Singleton { private static volatile Singleton instance = null; private Singleton() { }; public static synchronized Singleton getInstance() { if (instance == null) { synchronized (instance) { if (instance == null) { instance = new Singleton(); } } } return instance; }}
到此,我们就担保了3 天生器模式
定义:封装一个繁芜工具布局过程,并许可按步骤布局。
定义阐明: 我们可以将天生器模式理解为,假设我们有一个工具须要建立,这个工具是由多个组件(Component)组合而成,每个组件的建立都比较繁芜,但利用组件来建立所需的工具非常大略,以是我们就可以将构建繁芜组件的步骤与利用组件构建工具分离,利用builder模式可以建立。
3.1 模式的构造和代码示例
天生器模式构造中包括四种角色:
(1)产品(Product):详细生产器要布局的繁芜工具;
(2)抽象天生器(Bulider):抽象天生器是一个接口,该接口除了为创建一个Product工具的各个组件定义了多少个方法之外,还要定义返回Product工具的方法(定义布局步骤);
(3)详细生产器(ConcreteBuilder):实现Builder接口的类,详细天生器将实现Builder接口所定义的方法(生产各个组件);
(4)指挥者(Director):指挥者是一个类,该类须要含有Builder接口声明的变量。指挥者的职责是卖力向用户供应详细天生器,即指挥者将要求详细天生器类来布局用户所须要的Product工具,如果所要求的详细天生器成功地布局出Product工具,指挥者就可以让该详细生产器返回所布局的Product工具。(按照步骤组装部件,并返回Product)
举例(我们如果构建天生一台电脑,那么我们可能须要这么几个步骤(1)须要一个主机(2)须要一个显示器(3)须要一个键盘(4)须要一个鼠标)
虽然我们详细在构建一台主机的时候,每个工具的实际步骤是不一样的,比如,有的工具构建了i7cpu的主机,有的工具构建了i5cpu的主机,有的工具构建了普通键盘,有的工具构建了机器键盘等。但不管若何,你总是须要经由一个步骤便是构建一台主机,一台键盘。对付这个例子,我们就可以利用天生器模式来天生一台电脑,他须要通过多个步骤来天生。类图如下:
ComputerBuilder类定义布局步骤:
public abstract class ComputerBuilder { protected Computer computer; public Computer getComputer() { return computer; } public void buildComputer() { computer = new Computer(); System.out.println("天生了一台电脑!
!
!
"); } public abstract void buildMaster(); public abstract void buildScreen(); public abstract void buildKeyboard(); public abstract void buildMouse(); public abstract void buildAudio();}
HPComputerBuilder定义各个组件:
public class HPComputerBuilder extends ComputerBuilder { @Override public void buildMaster() { // TODO Auto-generated method stub computer.setMaster("i7,16g,512SSD,1060"); System.out.println("(i7,16g,512SSD,1060)的惠普主机"); } @Override public void buildScreen() { // TODO Auto-generated method stub computer.setScreen("1080p"); System.out.println("(1080p)的惠普显示屏"); } @Override public void buildKeyboard() { // TODO Auto-generated method stub computer.setKeyboard("cherry 青轴机器键盘"); System.out.println("(cherry 青轴机器键盘)的键盘"); } @Override public void buildMouse() { // TODO Auto-generated method stub computer.setMouse("MI 鼠标"); System.out.println("(MI 鼠标)的鼠标"); } @Override public void buildAudio() { // TODO Auto-generated method stub computer.setAudio("飞利浦 音响"); System.out.println("(飞利浦 音响)的音响"); }}
Director类对组件进行组装并天生产品
public class Director { private ComputerBuilder computerBuilder; public void setComputerBuilder(ComputerBuilder computerBuilder) { this.computerBuilder = computerBuilder; } public Computer getComputer() { return computerBuilder.getComputer(); } public void constructComputer() { computerBuilder.buildComputer(); computerBuilder.buildMaster(); computerBuilder.buildScreen(); computerBuilder.buildKeyboard(); computerBuilder.buildMouse(); computerBuilder.buildAudio(); }}
3.2 天生器模式的优缺陷
优点
将一个工具分解为各个组件将工具组件的布局封装起来可以掌握全体工具的天生过程缺陷
对不同类型的工具须要实现不同的详细布局器的类,这可能回答大大增加类的数量3.3 天生器模式与工厂模式的不同
天生器模式构建工具的时候,工具常日构建的过程中须要多个步骤,就像我们例子中的先有主机,再有显示屏,再有鼠标等等,天生器模式的浸染便是将这些繁芜的构建过程封装起来。工厂模式构建工具的时候常日就只有一个步骤,调用一个工厂方法就可以天生一个工具。
4 原型模式定义:通过复制现有实例来创建新的实例,无需知道相应类的信息。
大略地理解,实在便是当须要创建一个指定的工具时,我们刚好有一个这样的工具,但是又不能直策应用,我会clone一个一毛一样的新工具来利用;基本上这便是原型模式。关键字:Clone。
4.1 深拷贝和浅拷贝
浅复制:将一个工具复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原工具所指向的。
深复制:将一个工具复制后,不论是基本数据类型还有引用类型,都是重新创建的。大略来说,便是深复制进行了完备彻底的复制,而浅复制不彻底。clone明显是深复制,clone出来的工具是是不能去影响原型工具的
4.2 原型模式的构造和代码示例
Client:利用者
Prototype:接口(抽象类),声明具备clone能力,例如java中得Cloneable接口
ConcretePrototype:详细的原型类
可以看出设计模式还是比较大略的,重点在于Prototype接口和Prototype接口的实现类ConcretePrototype。原型模式的详细实现:一个原型类,只须要实现Cloneable接口,覆写clone方法,此处clone方法可以改成任意的名称,由于Cloneable接口是个空接口,你可以任意定义实现类的方法名,如cloneA或者cloneB,由于此处的重点是super.clone()这句话,super.clone()调用的是Object的clone()方法。
public class Prototype implements Cloneable { public Object clone() throws CloneNotSupportedException { Prototype proto = (Prototype) super.clone(); return proto; } }
举例(银行发送大量邮件,利用clone和不该用clone的韶光比拟):我们仿照创建一个工具须要耗费比较长的韶光,因此,在布局函数中我们让当前哨程sleep一会
public Mail(EventTemplate et) { this.tail = et.geteventContent(); this.subject = et.geteventSubject(); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
不该用clone,发送十个邮件
public static void main(String[] args) { int i = 0; int MAX_COUNT = 10; EventTemplate et = new EventTemplate("9月份信用卡账单", "国庆抽奖活动..."); long start = System.currentTimeMillis(); while (i < MAX_COUNT) { // 以下是每封邮件不同的地方 Mail mail = new Mail(et); mail.setContent(getRandString(5) + ",师长西席(女士):你的信用卡账单..." + mail.getTail()); mail.setReceiver(getRandString(5) + "@" + getRandString(8) + ".com"); // 然后发送邮件 sendMail(mail); i++; } long end = System.currentTimeMillis(); System.out.println("用时:" + (end - start)); }
用时:10001
利用clone,发送十个邮件
public static void main(String[] args) { int i = 0; int MAX_COUNT = 10; EventTemplate et = new EventTemplate("9月份信用卡账单", "国庆抽奖活动..."); long start=System.currentTimeMillis(); Mail mail = new Mail(et); while (i < MAX_COUNT) { Mail cloneMail = mail.clone(); mail.setContent(getRandString(5) + ",师长西席(女士):你的信用卡账单..." + mail.getTail()); mail.setReceiver(getRandString(5) + "@" + getRandString(8) + ".com"); sendMail(cloneMail); i++; } long end=System.currentTimeMillis(); System.out.println("用时:"+(end-start)); }
用时:1001
4.3 总结
原型模式的实质便是clone,可以办理构建繁芜工具的资源花费问题,能再某些场景中提升构建工具的效率;还有一个主要的用场便是保护性拷贝,可以通过返回一个拷贝工具的形式,实现只读的限定。
二、构造模式(7种)适配器模式、装饰器模式、代理模式、外不雅观模式、桥接模式、组合模式、享元模式。
5 适配器模式定义: 适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是肃清由于接口不匹配所造成的类的兼容性问题。
紧张分为三类:类的适配器模式、工具的适配器模式、接口的适配器模式。
5.1 类适配器模式
通过多重继续目标接口和被适配者类办法来实现适配
举例(将USB接口转为VGA接口),类图如下:
USBImpl的代码:
public class USBImpl implements USB{ @Override public void showPPT() { // TODO Auto-generated method stub System.out.println("PPT内容演示"); }}
AdatperUSB2VGA 首先继续USBImpl获取USB的功能,其次,实现VGA接口,表示该类的类型为VGA。
public class AdapterUSB2VGA extends USBImpl implements VGA { @Override public void projection() { super.showPPT(); }}
Projector将USB映射为VGA,只有VGA接口才可以连接上投影仪进行投影
public class Projector<T> { public void projection(T t) { if (t instanceof VGA) { System.out.println("开始投影"); VGA v = new VGAImpl(); v = (VGA) t; v.projection(); } else { System.out.println("接口不匹配,无法投影"); } }}
test代码
@Test public void test2(){ //通过适配器创建一个VGA工具,这个适配器实际是利用的是USB的showPPT()方法 VGA a=new AdapterUSB2VGA(); //进行投影 Projector p1=new Projector(); p1.projection(a); }
5.2 工具适配器模式
工具适配器和类适配器利用了不同的方法实现适配,工具适配器利用组合,类适配器利用继续。
举例(将USB接口转为VGA接口),类图如下:
public class AdapterUSB2VGA implements VGA { USB u = new USBImpl(); @Override public void projection() { u.showPPT(); }}
实现VGA接口,表示适配器类是VGA类型的,适配器方法中直策应用USB工具。
5.3 接口适配器模式
当不须要全部实现接供词给的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法供应一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求,它适用于一个接口不想利用其所有的方法的情形。
举例(将USB接口转为VGA接口,VGA中的b()和c()不会被实现),类图如下:
AdapterUSB2VGA抽象类
public abstract class AdapterUSB2VGA implements VGA { USB u = new USBImpl(); @Override public void projection() { u.showPPT(); } @Override public void b() { }; @Override public void c() { };}
AdapterUSB2VGA实现,不用去实现b()和c()方法。
public class AdapterUSB2VGAImpl extends AdapterUSB2VGA { public void projection() { super.projection(); }}
5.4 总结
总结一下三种适配器模式的运用处景:
类适配器模式:当希望将一个类转换成知足另一个新接口的类时,可以利用类的适配器模式,创建一个新类,继续原有的类,实现新的接口即可。
工具适配器模式:当希望将一个工具转换成知足另一个新接口的工具时,可以创建一个Wrapper类,持有原类的一个实例,在Wrapper类的方法中,调用实例的方法就行。
接口适配器模式:当不肯望实现一个接口中所有的方法时,可以创建一个抽象类Wrapper,实现所有方法,我们写别的类的时候,继续抽象类即可。
命名规则:
我个人理解,三种命名办法,是根据 src因此若何的形式给到Adapter(在Adapter里的形式)来命名的。
类适配器,以类给到,在Adapter里,便是将src当做类,继续,
工具适配器,以工具给到,在Adapter里,将src作为一个工具,持有。
接口适配器,以接口给到,在Adapter里,将src作为一个接口,实现。
利用选择:
根据合成复用原则,组合大于继续。因此,类的适配器模式该当少用。
6 装饰者模式定义:动态的将新功能附加到工具上。在工具功能扩展方面,它比继续更有弹性。
6.1 装饰者模式构造图与代码示例
1.Component(被装饰工具的基类)
定义一个工具接口,可以给这些工具动态地添加职责。
2.ConcreteComponent(详细被装饰工具)
定义一个工具,可以给这个工具添加一些职责。
3.Decorator(装饰者抽象类)
坚持一个指向Component实例的引用,并定义一个与Component接口同等的接口。
4.ConcreteDecorator(详细装饰者)
详细的装饰工具,给内部持有的详细被装饰工具,增加详细的职责。
被装饰工具和润色者继续自同一个超类
举例(咖啡馆订单项目:1)、咖啡种类:Espresso、ShortBlack、LongBlack、Decaf2)、调料(装饰者):Milk、Soy、Chocolate),类图如下:
被装饰的工具和装饰者都继续自同一个超类
public abstract class Drink { public String description=""; private float price=0f;; public void setDescription(String description) { this.description=description; } public String getDescription() { return description+"-"+this.getPrice(); } public float getPrice() { return price; } public void setPrice(float price) { this.price=price; } public abstract float cost(); }
被装饰的工具,不用去改造。原来怎么样写,现在还是怎么写。
public class Coffee extends Drink { @Override public float cost() { // TODO Auto-generated method stub return super.getPrice(); } }
装饰者
装饰者不仅要考虑自身,还要考虑被它润色的工具,它是在被润色的工具上连续添加润色。例如,咖啡里面加牛奶,再加巧克力。加糖后价格为coffee+milk。再加牛奶价格为coffee+milk+chocolate。
public class Decorator extends Drink { private Drink Obj; public Decorator(Drink Obj) { this.Obj = Obj; }; @Override public float cost() { // TODO Auto-generated method stub return super.getPrice() + Obj.cost(); } @Override public String getDescription() { return super.description + "-" + super.getPrice() + "&&" + Obj.getDescription(); }}
装饰者实例化(加牛奶)。这里面要对被润色的工具进行实例化。
public class Milk extends Decorator { public Milk(Drink Obj) { super(Obj); // TODO Auto-generated constructor stub super.setDescription("Milk"); super.setPrice(2.0f); }}
coffee店:初始化一个被润色工具,润色者实例须要对被修正者实例化,才能对详细的被润色者进行润色。
public class CoffeeBar { public static void main(String[] args) { Drink order; order = new Decaf(); System.out.println("order1 price:" + order.cost()); System.out.println("order1 desc:" + order.getDescription()); System.out.println(""); order = new LongBlack(); order = new Milk(order); order = new Chocolate(order); order = new Chocolate(order); System.out.println("order2 price:" + order.cost()); System.out.println("order2 desc:" + order.getDescription()); }}
6.2 总结
装饰者和被装饰者之间必须是一样的类型,也便是要有共同的超类。在这里运用继续并不是实现方法的复制,而是实现类型的匹配。由于装饰者和被装饰者是同一个类型,因此装饰者可以取代被装饰者,这样就使被装饰者拥有了装饰者独占的行为。根据装饰者模式的理念,我们可以在任何时候,实现新的装饰者增加新的行为。如果是用继续,每当须要增加新的行为时,就要修正原程序了。
7 代理模式定义:代理模式给某一个工具供应一个代理工具,并由代理工具掌握对原工具的引用。普通的来讲代理模式便是我们生活中常见的中介。
举个例子来解释:如果说我现在想买一辆二手车,虽然我可以自己去找车源,做质量检测等一系列的车辆过户流程,但是这确实太摧残浪费蹂躏我得韶光和精力了。我只是想买一辆车而已为什么我还要额外做这么多事呢?于是我就通过中介公司来买车,他们来给我找车源,帮我办理车辆过户流程,我只是卖力选择自己喜好的车,然后付钱就可以了。用图表示如下:
7.1 为什么要用代理模式?
中介隔离浸染:在某些情形下,一个客户类不想或者不能直接引用一个委托工具,而代理类工具可以在客户类和委托工具之间起到中介的浸染,其特色是代理类和委托类实现相同的接口。
开闭原则,增加功能:代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只须要修正代理类而不须要再修正委托类,符合代码设计的开闭原则。代理类紧张卖力为委托类预处理、过滤、把转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现做事,而是同过调用委托类的干系方法,来供应特定的做事。真正的业务功能还是由委托类来实现,但是可以在业务功能实行的前后加入一些公共的做事。例如我们想给项目加入缓存、日志这些功能,我们就可以利用代理类来完成,而没必要打开已经封装好的委托类。
代理模式分为三类:1. 静态代理 2. 动态代理 3. CGLIB代理
7.2 静态代理
举例(买房),类图如下:
第一步:创建做事类接口
public interface BuyHouse { void buyHosue();}
第二步:实现做事接口
public class BuyHouseImpl implements BuyHouse { @Override public void buyHosue() { System.out.println("我要买房"); }}
第三步:创建代理类
public class BuyHouseProxy implements BuyHouse { private BuyHouse buyHouse; public BuyHouseProxy(final BuyHouse buyHouse) { this.buyHouse = buyHouse; } @Override public void buyHosue() { System.out.println("买房前准备"); buyHouse.buyHosue(); System.out.println("买房后装修"); }}
总结:
优点:可以做到在符合开闭原则的情形下对目标工具进行功能扩展。
缺陷: 代理工具与目标工具要实现相同的接口,我们得为每一个做事都得创建代理类,事情量太大,不易管理。同时接口一旦发生改变,代理类也得相应修正。
7.3 动态代理
动态代理有以下特点:
1.代理工具,不须要实现接口
2.代理工具的天生,是利用JDK的API,动态的在内存中构建代理工具(须要我们指定创建代理工具/目标工具实现的接口的类型)
代理类不用再实现接口了。但是,哀求被代理工具必须有接口。
动态代理实现:
Java.lang.reflect.Proxy类可以直接天生一个代理工具
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)天生一个代理工具
参数1:ClassLoader loader 代理工具的类加载器 一样平常利用被代理工具的类加载器参数2:Class<?>[] interfaces 代理工具的要实现的接口 一样平常利用的被代理工具实现的接口参数3:InvocationHandler h (接口)实行处理类InvocationHandler中的invoke(Object proxy, Method method, Object[] args)方法:调用代理类的任何方法,此方法都会实行
参数3.1:代理工具(慎用)参数3.2:当前实行的方法参数3.3:当前实行的方法运行时通报过来的参数第一步:编写动态处理器
public class DynamicProxyHandler implements InvocationHandler { private Object object; public DynamicProxyHandler(final Object object) { this.object = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("买房前准备"); Object result = method.invoke(object, args); System.out.println("买房后装修"); return result; }}
第二步:编写测试类
public class DynamicProxyTest { public static void main(String[] args) { BuyHouse buyHouse = new BuyHouseImpl(); BuyHouse proxyBuyHouse = (BuyHouse) Proxy.newProxyInstance(BuyHouse.class.getClassLoader(), new Class[]{BuyHouse.class}, new DynamicProxyHandler(buyHouse)); proxyBuyHouse.buyHosue(); }}
动态代理总结:虽然相对付静态代理,动态代理大大减少了我们的开拓任务,同时减少了对业务接口的依赖,降落了耦合度。但是还是有一点点小小的遗憾之处,那便是它始终无法摆脱仅支持interface代理的桎梏(我们要利用被代理的工具的接口),由于它的设计注定了这个遗憾。
7.4 CGLIB代理
CGLIB 事理:动态天生一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采取方法拦截的技能拦截所有父类方法的调用,顺势织入横切逻辑。它比利用java反射的JDK动态代理要快。
CGLIB 底层:利用字节码处理框架ASM,来转换字节码并天生新的类。不鼓励直策应用ASM,由于它哀求你必须对JVM内部构造包括class文件的格式和指令集都很熟习。
CGLIB缺陷:对付final方法,无法进行代理。
CGLIB的实现步骤:
第一步:建立拦截器
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("买房前准备"); Object result = methodProxy.invoke(object, args); System.out.println("买房后装修"); return result; }
参数:Object为由CGLib动态天生的代理类实例,Method为上文中实体类所调用的被代理的方法引用,Object[]为参数值列表,MethodProxy为天生的代理类对方法的代理引用。
返回:从代理实例的方法调用返回的值。
个中,proxy.invokeSuper(obj,arg) 调用代理类实例上的proxy方法的父类方法(即实体类TargetObject中对应的方法)
第二步: 天生动态代理类
public class CglibProxy implements MethodInterceptor { private Object target; public Object getInstance(final Object target) { this.target = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.target.getClass()); enhancer.setCallback(this); return enhancer.create(); } public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("买房前准备"); Object result = methodProxy.invoke(object, args); System.out.println("买房后装修"); return result; }}
这里Enhancer类是CGLib中的一个字节码增强器,它可以方便的对你想要处理的类进行扩展,往后会常常看到它。
首先将被代理类TargetObject设置成父类,然后设置拦截器TargetInterceptor,末了实行enhancer.create()动态天生一个代理类,并从Object逼迫转型成父类型TargetObject。
第三步:测试
public class CglibProxyTest { public static void main(String[] args){ BuyHouse buyHouse = new BuyHouseImpl(); CglibProxy cglibProxy = new CglibProxy(); BuyHouseImpl buyHouseCglibProxy = (BuyHouseImpl) cglibProxy.getInstance(buyHouse); buyHouseCglibProxy.buyHosue(); }}
CGLIB代理总结: CGLIB创建的动态代理工具比JDK创建的动态代理工具的性能更高,但是CGLIB创建代理工具时所花费的韶光却比JDK多得多。以是对付单例的工具,由于无需频繁创建工具,用CGLIB得当,反之利用JDK办法要更为得当一些。同时由于CGLib由于是采取动态创建子类的方法,对付final润色的方法无法进行代理。
8 外不雅观模式定义: 隐蔽了系统的繁芜性,并向客户端供应了一个可以访问系统的接口。
8.1 模式构造和代码示例
大略来说,该模式便是把一些繁芜的流程封装成一个接口供给外部用户更大略的利用。这个模式中,设计到3个角色。
1).门面角色:外不雅观模式的核心。它被客户角色调用,它熟习子系统的功能。内部根据客户角色的需求预定了几种功能的组合。(客户调用,同时自身调用子系统功能)
2).子系统角色:实现了子系统的功能。它对客户角色和Facade时未知的。它内部可以有系统内的相互交互,也可以由供外界调用的接口。(实现详细功能)
3).客户角色:通过调用Facede来完成要实现的功能(调用门面角色)。
举例(每个Computer都有CPU、Memory、Disk。在Computer开启和关闭的时候,相应的部件也会开启和关闭),类图如下:
首先是子系统类:
public class CPU { public void start() {System.out.println("cpu is start...");} public void shutDown() {System.out.println("CPU is shutDown...");}} public class Disk {public void start() {System.out.println("Disk is start...");} public void shutDown() {System.out.println("Disk is shutDown...");}} public class Memory {public void start() {System.out.println("Memory is start...");} public void shutDown() {System.out.println("Memory is shutDown...");}}
然后是,门面类Facade
public class Computer { private CPU cpu;private Memory memory;private Disk disk; public Computer() {cpu = new CPU();memory = new Memory();disk = new Disk();} public void start() {System.out.println("Computer start begin");cpu.start();disk.start();memory.start();System.out.println("Computer start end");} public void shutDown() {System.out.println("Computer shutDown begin");cpu.shutDown();disk.shutDown();memory.shutDown();System.out.println("Computer shutDown end...");}}
末了为,客户角色
public class Client { public static void main(String[] args) {Computer computer = new Computer();computer.start();System.out.println("=================");computer.shutDown();} }
8.2 优点
- 疏松耦合
使得客户端和子系统之间解耦,让子系统内部的模块功能更随意马虎扩展和掩护;
- 大略易用
客户端根本不须要知道子系统内部的实现,或者根本不须要知道子系统内部的构成,它只须要跟Facade类交互即可。
- 更好的划分访问层次
有些方法是对系统外的,有些方法是系统内部相互交互的利用的。子系统把那些暴露给外部的功能集中到门面中,这样就可以实现客户真个利用,很好的隐蔽了子系统内部的细节。
9 桥接模式定义: 将抽象部分与它的实现部分分离,使它们都可以独立地变革。
9.1 案例
看下图手机与手机软件的类图
增加一款新的手机软件,须要在所有手机品牌类下添加对应的手机软件类,当手机软件种类较多时,将导致类的个数急剧膨胀,难以掩护
手机和手机中的软件是什么关系?
手机中的软件从实质上来说并不是一种手机,手机软件运行在手机中,是一种包含与被包含关系,而不是一种父与子或者说一样平常与分外的关系,通过继续手机类实现手机软件类的设计是违反一样平常规律的。
如果Oppo手机实现了wifi功能,继续它的Oppo运用商城也会继续wifi功能,并且Oppo手机类的任何变动,都会影响其子类
换一种办理思路
从类图上看起来更像是手机软件类图,涉及得手机本身干系的功能,比如说:wifi功能,放到哪个类中实现呢?放到OppoAppStore中实现显然是不得当的
引起全体构造变革的元素有两个,一个是手机品牌,一个是手机软件,以是我们将这两个点抽出来,分别进行封装
9.2 桥接模式构造和代码示例
类图:
实现:
public interface Software {public void run(); }public class AppStore implements Software { @Override public void run() { System.out.println("run app store"); }}public class Camera implements Software { @Override public void run() { System.out.println("run camera"); }}
抽象:
public abstract class Phone { protected Software software; public void setSoftware(Software software) {this.software = software;} public abstract void run(); }public class Oppo extends Phone { @Override public void run() { Coming Soon(); }}public class Vivo extends Phone { @Override public void run() { Coming Soon(); }}
比拟最初的设计,将抽象部分(手机)与它的实现部分(手机软件类)分离,将实现部分抽象成单独的类,使它们都可以独立地变革。全体类图看起来像一座桥,以是称为桥接模式
继续是一种强耦合关系,子类的实现与它的父类有非常紧密的依赖关系,父类的任何变革 都会导致子类发生变革,因此继续或者说强耦合关系严重影响了类的灵巧性,并终极限定了可复用性
从桥接模式的设计上我们可以看出聚合是一种比继续要弱的关联关系,手机类和软件类都可独立的进行变革,不会相互影响
9.3 适用场景
桥接模式常日适用于以了局景。
当一个类存在两个独立变革的维度,且这两个维度都须要进行扩展时。当一个别系不肯望利用继续或由于多层次继续导致系统类的个数急剧增加时。当一个别系须要在构件的抽象化角色和详细化角色之间增加更多的灵巧性时。9.4 优缺陷
优点:
(1)在很多情形下,桥接模式可以取代多层继续方案,多层继续方案违背了“单一职责原则”,复用性较差,且类的个数非常多,桥接模式是比多层继续方案更好的办理方法,它极大减少了子类的个数。
(2)桥接模式提高了系统的可扩展性,在两个变革维度中任意扩展一个维度,都不须要修正原有系统,符合“开闭原则”。
缺陷:
桥接模式的利用会增加系统的理解与设计难度,由于关联关系建立在抽象层,哀求开拓者一开始就针对抽象层进行设计与编程。
10 组合模式定义:有时又叫作部分-整体模式,它是一种将工具组合成树状的层次构造的模式,用来表示“部分-整体”的关系,利用户对单个工具和组合工具具有同等的访问性。
意图:将工具组合成树形构造以表示"部分-整体"的层次构造。组合模式使得用户对单个工具和组合工具的利用具有同等性。
紧张办理:它在我们树型构造的问题中,模糊了大略元素和繁芜元素的观点,客户程序可以向处理大略元素一样来处理繁芜元素,从而使得客户程序与繁芜元素的内部构造解耦。
何时利用: 1、您想表示工具的部分-整体层次构造(树形构造)。 2、您希望用户忽略组合工具与单个工具的不同,用户将统一地利用组合构造中的所有工具。
如何办理:树枝和叶子实现统一接口,树枝内部组合该接口。
关键代码:树枝内部组合该接口,并且含有内部属性 List,里面放 Component。
组合模式的紧张优点有:
组合模式使得客户端代码可以同等地处理单个工具和组合工具,无须关心自己处理的是单个工具,还是组合工具,这简化了客户端代码;更随意马虎在组合体内加入新的工具,客户端不会由于加入了新的工具而变动源代码,知足“开闭原则”;其紧张缺陷是:
设计较繁芜,客户端须要花更多韶光理清类之间的层次关系;不随意马虎限定容器中的构件;不随意马虎用继续的方法来增加构件的新功能;10.1 模式构造和代码示例
抽象构件(Component)角色:它的紧张浸染是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。在透明式的组合模式中抽象构件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理事情由树枝构件完成。树叶构件(Leaf)角色:是组合中的叶节点工具,它没有子节点,用于实现抽象构件角色中 声明的公共接口。树枝构件(Composite)角色:是组合中的分支节点工具,它有子节点。它实现了抽象构件角色中声明的接口,它的紧张浸染是存储和管理子部件,常日包含 Add()、Remove()、GetChild() 等方法
举例(访问一颗树),类图如下:
1 组件
public interface Component { public void add(Component c); public void remove(Component c); public Component getChild(int i); public void operation(); }
2 叶子
public class Leaf implements Component{ private String name;public Leaf(String name) {this.name = name;} @Overridepublic void add(Component c) {} @Overridepublic void remove(Component c) {} @Overridepublic Component getChild(int i) {// TODO Auto-generated method stubreturn null;} @Overridepublic void operation() {// TODO Auto-generated method stub System.out.println("树叶"+name+":被访问!
"); } }
3 树枝
public class Composite implements Component { private ArrayList<Component> children = new ArrayList<Component>(); public void add(Component c) {children.add(c);} public void remove(Component c) {children.remove(c);} public Component getChild(int i) {return children.get(i);} public void operation() {for (Object obj : children) {((Component) obj).operation();}}}
11 享元模式
定义:通过共享的办法高效的支持大量细粒度的工具。
紧张办理:在有大量工具时,有可能会造成内存溢出,我们把个中共同的部分抽象出来,如果有相同的业务要求,直接返回在内存中已有的工具,避免重新创建。
何时利用:
1、系统中有大量工具。
2、这些工具花费大量内存。
3、这些工具的状态大部分可以外部化。
4、这些工具可以按照内蕴状态分为很多组,当把外蕴工具从工具中剔除出来时,每一组工具都可以用一个工具来代替。
5、系统不依赖于这些工具身份,这些工具是不可分辨的。
如何办理:用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的工具。
关键代码:用 HashMap 存储这些工具。
运用实例: 1、JAVA 中的 String,如果有则返回,如果没有则创建一个字符串保存在字符串缓存池里面。
优点:大大减少工具的创建,降落系统的内存,使效率提高。
缺陷:提高了系统的繁芜度,须要分离出外部状态和内部状态,而且外部状态具有固有化的性子,不应该随着内部状态的变革而变革,否则会造成系统的混乱。
大略来说,我们抽取出一个工具的外部状态(不能共享)和内部状态(可以共享)。然后根据外部状态的决定是否创建内部状态工具。内部状态工具是通过哈希表保存的,当外部状态相同的时候,不再重复的创建内部状态工具,从而减少要创建工具的数量。
11.1 享元模式的构造图和代码示例
1、Flyweight (享元抽象类):一样平常是接口或者抽象类,定义了享元类的公共方法。这些方法可以分享内部状态的数据,也可以调用这些方法修正外部状态。
2、ConcreteFlyweight(详细享元类):详细享元类实现了抽象享元类的方法,为享元工具开辟了内存空间来保存享元工具的内部数据,同时可以通过和单例模式结合只创建一个享元工具。
3、FlyweightFactory(享元工厂类):享元工厂类创建并且管理享元类,享元工厂类针对享元类来进行编程,通过供应一个享元池来进行享元工具的管理。一样平常享元池设计成键值对,或者其他的存储构造来存储。当客户端进行享元工具的要求时,如果享元池中有对应的享元工具则直接返回对应的工具,否则工厂类创建对应的享元工具并保存到享元池。
举例(JAVA 中的 String,如果有则返回,如果没有则创建一个字符串保存在字符串缓存池里面)。类图如下:
(1)创建享元工具接口
public interface IFlyweight { void print();}
(2)创建详细享元工具
public class Flyweight implements IFlyweight { private String id; public Flyweight(String id){ this.id = id; } @Override public void print() { System.out.println("Flyweight.id = " + getId() + " ..."); } public String getId() { return id; }}
(3)创建工厂,这里要特殊把稳,为了避免享元工具被重复创建,我们利用HashMap中的key值担保其唯一。
public class FlyweightFactory { private Map<String, IFlyweight> flyweightMap = new HashMap(); public IFlyweight getFlyweight(String str){ IFlyweight flyweight = flyweightMap.get(str); if(flyweight == null){ flyweight = new Flyweight(str); flyweightMap.put(str, flyweight); } return flyweight; } public int getFlyweightMapSize(){ return flyweightMap.size(); }}
(4)测试,我们创建三个字符串,但是只会产生两个享元工具
public class MainTest {public static void main(String[] args) { FlyweightFactory flyweightFactory = new FlyweightFactory(); IFlyweight flyweight1 = flyweightFactory.getFlyweight("A"); IFlyweight flyweight2 = flyweightFactory.getFlyweight("B"); IFlyweight flyweight3 = flyweightFactory.getFlyweight("A"); flyweight1.print(); flyweight2.print(); flyweight3.print(); System.out.println(flyweightFactory.getFlyweightMapSize()); } }
三、关系模式(11种)
先来张图,看看这11中模式的关系:
第一类:通过父类与子类的关系进行实现。
第二类:两个类之间。
第三类:类的状态。
第四类:通过中间类
12 策略模式
定义: 策略模式定义了一系列算法,并将每个算法封装起来,使他们可以相互更换,且算法的变革不会影响到利用算法的客户。
意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互更换。
紧张办理:在有多种算法相似的情形下,利用 if...else 所带来的繁芜和难以掩护。
何时利用:一个别系有许多许多类,而区分它们的只是他们直接的行为。
如何办理:将这些算法封装成一个一个的类,任意地更换。
关键代码:实现同一个接口。
优点: 1、算法可以自由切换。 2、避免利用多重条件判断。 3、扩展性良好。
缺陷: 1、策略类会增多。 2、所有策略类都须要对外暴露。
12.1 策略模式构造和示例代码
抽象策略角色: 这个是一个抽象的角色,常日情形下利用接口或者抽象类去实现。比拟来说,便是我们的Comparator接口。
详细策略角色: 包装了详细的算法和行为。比拟来说,便是实现了Comparator接口的实现一组实现类。
环境角色: 内部会持有一个抽象角色的引用,给客户端调用。
举例如下( 实现一个加减的功能),类图如下:
1、定义抽象策略角色
public interface Strategy { public int calc(int num1,int num2);}
2、定义详细策略角色
public class AddStrategy implements Strategy { @Overridepublic int calc(int num1, int num2) {// TODO Auto-generated method stubreturn num1 + num2;} }public class SubstractStrategy implements Strategy { @Overridepublic int calc(int num1, int num2) {// TODO Auto-generated method stubreturn num1 - num2;} }
3、环境角色
public class Environment {private Strategy strategy; public Environment(Strategy strategy) {this.strategy = strategy;} public int calculate(int a, int b) {return strategy.calc(a, b);} }
4、测试
public class MainTest {public static void main(String[] args) {Environment environment=new Environment(new AddStrategy());int result=environment.calculate(20, 5);System.out.println(result);Environment environment1=new Environment(new SubstractStrategy());int result1=environment1.calculate(20, 5);System.out.println(result1);} }
13 模板模式
定义:定义一个操作中算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变算法的构造即可重定义该算法的某些特定步骤。
普通点的理解便是 :完成一件事情,有固定的数个步骤,但是每个步骤根据工具的不同,而实现细节不同;就可以在父类中定义一个完成该事情的总方法,按照完成事宜须要的步骤去调用其每个步骤的实现方法。每个步骤的详细实现,由子类完成。
13.1 模式构造和代码示例
抽象父类(AbstractClass):实现了模板方法,定义了算法的骨架。
详细类(ConcreteClass):实现抽象类中的抽象方法,即不同的工具的详细实现细节。23 种设计模式详解(全23种)详细类(ConcreteClass):实现抽象类中的抽象方法,即不同的工具的详细实现细节。
举例( 我们做菜可以分为三个步骤 (1)备料 (2)详细做菜 (3)盛菜端给客人享用,这三部便是算法的骨架 ;然而做不同菜须要的料,做的方法,以及如何艳服给客人享用都是不同的这个便是不同的实现细节。)。类图如下:
a. 先来写一个抽象的做菜父类:
public abstract class Dish { / 详细的全体过程 / protected void dodish(){ this.preparation(); this.doing(); this.carriedDishes(); } / 备料 / public abstract void preparation(); / 做菜 / public abstract void doing(); / 上菜 / public abstract void carriedDishes ();}
b. 下来做两个番茄炒蛋(EggsWithTomato)和红烧肉(Bouilli)实现父类中的抽象方法
public class EggsWithTomato extends Dish { @Overridepublic void preparation() {System.out.println("洗并切西红柿,打鸡蛋。");} @Overridepublic void doing() {System.out.println("鸡蛋倒入锅里,然后倒入西红柿一起炒。");} @Overridepublic void carriedDishes() {System.out.println("将炒好的西红寺鸡蛋装入碟子里,端给客人吃。");} }public class Bouilli extends Dish{ @Override public void preparation() { System.out.println("切猪肉和土豆。"); } @Override public void doing() { System.out.println("将切好的猪肉倒入锅中炒一会然后倒入土豆连炒带炖。"); } @Override public void carriedDishes() { System.out.println("将做好的红烧肉盛进碗里端给客人吃。"); } }
c. 在测试类中我们来做菜:
public class MainTest {public static void main(String[] args) {Dish eggsWithTomato = new EggsWithTomato();eggsWithTomato.dodish(); System.out.println("-----------------------------"); Dish bouilli = new Bouilli();bouilli.dodish();} }
13.2 模板模式的优点和缺陷
优点:
(1)详细细节步骤实现定义在子类中,子类定义详细处理算法是不会改变算法整体构造。
(2)代码复用的基本技能,在数据库设计中尤为主要。
(3)存在一种反向的掌握构造,通过一个父类调用其子类的操作,通过子类对父类进行扩展增加新的行为,符合“开闭原则”。
缺陷:
每个不同的实现都须要定义一个子类,会导致类的个数增加,系统更加弘大。
14 不雅观察者模式定义: 定义工具间的一种一对多的依赖关系,当一个工具的状态发生改变时,所有依赖于它的工具都得到关照并被自动更新。
紧张办理:一个工具状态改变给其他工具关照的问题,而且要考虑到易用和低耦合,担保高度的协作。
何时利用:一个工具(目标工具)的状态发生改变,所有的依赖工具(不雅观察者工具)都将得到关照,进行广播关照。
如何办理:利用面向工具技能,可以将这种依赖关系弱化。
关键代码:在抽象类里有一个 ArrayList 存放不雅观察者们。
优点:
1、不雅观察者和被不雅观察者是抽象耦合的。
2、建立一套触发机制。
缺陷:
1、如果一个被不雅观察者工具有很多的直接和间接的不雅观察者的话,将所有的不雅观察者都关照到会花费很多韶光。
2、如果在不雅观察者和不雅观察目标之间有循环依赖的话,不雅观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
3、不雅观察者模式没有相应的机制让不雅观察者知道所不雅观察的目标工具是怎么发生变革的,而仅仅只是知道不雅观察目标发生了变革。
14.1 模式构造图和代码示例
抽象被不雅观察者角色:也便是一个抽象主题,它把所有对不雅观察者工具的引用保存在一个凑集中,每个主题都可以有任意数量的不雅观察者。抽象主题供应一个接口,可以增加和删除不雅观察者角色。一样平常用一个抽象类和接口来实现。抽象不雅观察者角色:为所有的详细不雅观察者定义一个接口,在得到主题关照时更新自己详细被不雅观察者角色:也便是一个详细的主题,在集体主题的内部状态改变时,所有登记过的不雅观察者发出关照。详细不雅观察者角色:实现抽象不雅观察者角色所须要的更新接口,一边使本身的状态与制图的状态相折衷。
举例(有一个微信"大众号做事,禁绝时发布一些,关注"大众号就可以收到推送,取消关注就收不到推送。)类图如下:
1、定义一个抽象被不雅观察者接口
public interface Subject { public void registerObserver(Observer o); public void removeObserver(Observer o); public void notifyObserver(); }
2、定义一个抽象不雅观察者接口
public interface Observer {public void update(String message); }
3、定义被不雅观察者,实现了Observerable接口,对Observerable接口的三个方法进行了详细实现,同时有一个List凑集,用以保存注册的不雅观察者,等须要关照不雅观察者时,遍历该凑集即可。
public class WechatServer implements Subject { private List<Observer> list;private String message; public WechatServer() {list = new ArrayList<Observer>();} @Overridepublic void registerObserver(Observer o) {// TODO Auto-generated method stublist.add(o);} @Overridepublic void removeObserver(Observer o) {// TODO Auto-generated method stubif (!list.isEmpty()) {list.remove(o);}} @Overridepublic void notifyObserver() {// TODO Auto-generated method stubfor (Observer o : list) {o.update(message);}} public void setInfomation(String s) {this.message = s;System.out.println("微信服务更新: " + s);// 更新,关照所有不雅观察者notifyObserver();} }
4、定义详细不雅观察者,微信公众号的详细不雅观察者为用户User
public class User implements Observer { private String name;private String message; public User(String name) {this.name = name;} @Overridepublic void update(String message) {this.message = message;read();} public void read() {System.out.println(name + " 收到推送: " + message);} }
5、编写一个测试类
public class MainTest { public static void main(String[] args) { WechatServer server = new WechatServer(); Observer userZhang = new User("ZhangSan"); Observer userLi = new User("LiSi"); Observer userWang = new User("WangWu"); server.registerObserver(userZhang); server.registerObserver(userLi); server.registerObserver(userWang); server.setInfomation("PHP是天下上最好用的措辞!
15 迭代器模式
"); System.out.println("----------------------------------------------"); server.removeObserver(userZhang); server.setInfomation("JAVA是天下上最好用的措辞!
"); } }
定义:供应一种方法顺序访问一个聚合工具中各个元素, 而又无须暴露该工具的内部表示。
大略来说,不同种类的工具可能须要不同的遍历办法,我们对每一种类型的工具配一个迭代器,末了多个迭代器合成一个。
紧张办理:不同的办法来遍历全体整合工具。
何时利用:遍历一个聚合工具。
如何办理:把在元素之间游走的任务交给迭代器,而不是聚合工具。
关键代码:定义接口:hasNext, next。
运用实例:JAVA 中的 iterator。
优点: 1、它支持以不同的办法遍历一个聚合工具。 2、迭代器简化了聚合类。 3、在同一个聚合上可以有多个遍历。 4、在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修正原有代码。
缺陷:由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类须要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的繁芜性。
15.1 模式构造和代码示例
(1)迭代器角色(Iterator):定义遍历元素所须要的方法,一样平常来说会有这么三个方法:取得下一个元素的方法next(),判断是否遍历结束的方法hasNext()),移出当前工具的方法remove(),
(2)详细迭代器角色(Concrete Iterator):实现迭代器接口中定义的方法,完成凑集的迭代。
(3)容器角色(Aggregate): 一样平常是一个接口,供应一个iterator()方法,例如java中的Collection接口,List接口,Set接口等
(4)详细容器角色(ConcreteAggregate):便是抽象容器的详细实现类,比如List接口的有序列表实现ArrayList,List接口的链表实现LinkList,Set接口的哈希列表的实现HashSet等。
举例(咖啡厅和中餐厅合并,他们两个餐厅的菜单一个是数组保存的,一个是ArrayList保存的。遍历办法不一样,利用迭代器聚合访问,只须要一种办法)
1 迭代器接口
public interface Iterator {public boolean hasNext();public Object next();}
2 咖啡店菜单和咖啡店菜单遍历器
public class CakeHouseMenu {private ArrayList<MenuItem> menuItems;public CakeHouseMenu() {menuItems = new ArrayList<MenuItem>();addItem("KFC Cake Breakfast","boiled eggs&toast&cabbage",true,3.99f);addItem("MDL Cake Breakfast","fried eggs&toast",false,3.59f);addItem("Stawberry Cake","fresh stawberry",true,3.29f);addItem("Regular Cake Breakfast","toast&sausage",true,2.59f);} private void addItem(String name, String description, boolean vegetable,float price) {MenuItem menuItem = new MenuItem(name, description, vegetable, price);menuItems.add(menuItem);} public Iterator getIterator(){return new CakeHouseIterator() ;}class CakeHouseIterator implements Iterator {private int position=0;public CakeHouseIterator(){ position=0;} @Overridepublic boolean hasNext() {// TODO Auto-generated method stubif(position<menuItems.size()){return true;}return false;} @Overridepublic Object next() {// TODO Auto-generated method stubMenuItem menuItem =menuItems.get(position);position++;return menuItem;}};//鍏朵粬鍔熻兘浠g爜}
3 中餐厅菜单和中餐厅菜单遍历器
public class DinerMenu {private final static int Max_Items = 5;private int numberOfItems = 0;private MenuItem[] menuItems; public DinerMenu() {menuItems = new MenuItem[Max_Items];addItem("vegetable Blt", "bacon&lettuce&tomato&cabbage", true, 3.58f);addItem("Blt", "bacon&lettuce&tomato", false, 3.00f);addItem("bean soup", "bean&potato salad", true, 3.28f);addItem("hotdog", "onions&cheese&bread", false, 3.05f); } private void addItem(String name, String description, boolean vegetable,float price) {MenuItem menuItem = new MenuItem(name, description, vegetable, price);if (numberOfItems >= Max_Items) {System.err.println("sorry,menu is full!can not add another item");} else {menuItems[numberOfItems] = menuItem;numberOfItems++;} } public Iterator getIterator() {return new DinerIterator();} class DinerIterator implements Iterator {private int position; public DinerIterator() {position = 0;} @Overridepublic boolean hasNext() {// TODO Auto-generated method stubif (position < numberOfItems) {return true;}return false;} @Overridepublic Object next() {// TODO Auto-generated method stubMenuItem menuItem = menuItems[position];position++;return menuItem;}};}
4 女做事员
public class Waitress {private ArrayList<Iterator> iterators = new ArrayList<Iterator>(); public Waitress() { } public void addIterator(Iterator iterator) {iterators.add(iterator); } public void printMenu() {Iterator iterator;MenuItem menuItem;for (int i = 0, len = iterators.size(); i < len; i++) {iterator = iterators.get(i); while (iterator.hasNext()) {menuItem = (MenuItem) iterator.next();System.out.println(menuItem.getName() + "" + menuItem.getPrice() + "" + menuItem.getDescription()); } } } public void printBreakfastMenu() { } public void printLunchMenu() { } public void printVegetableMenu() { }}
16 任务链模式
定义:如果有多个工具有机会处理要求,任务链可使要求的发送者和接管者解耦,要求沿着任务链通报,直到有一个工具处理了它为止。
紧张办理:职责链上的处理者卖力处理要求,客户只须要将要求发送到职责链上即可,无须关心要求的处理细节和要求的通报,以是职责链将要求的发送者和要求的处理者解耦了。
何时利用:在处理的时候以过滤很多道。
如何办理:拦截的类都实现统一接口。
关键代码:Handler 里面聚合它自己,在 HandlerRequest 里判断是否得当,如果没达到条件则向下通报,向谁通报之前 set 进去。
16.1 模式的构造和代码示例
抽象处理者(Handler)角色:定义一个处理要求的接口,包含抽象处理方法和一个后继连接。详细处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次要求,如果可以处理要求则处理,否则将该要求转给它的后继者。客户类(Client)角色:创建处理链,并向链头的详细处理者工具提交要求,它不关心处理细节和要求的通报过程。
举例(购买要求决策,价格不同要由不同的级别决定:组长、部长、副部、总裁)。类图如下:
1 决策者抽象类,包含对要求处理的函数,同时还包含指定下一个决策者的函数
public abstract class Approver { Approver successor; String Name;public Approver(String Name){this.Name=Name;}public abstract void ProcessRequest( PurchaseRequest request);public void SetSuccessor(Approver successor) {// TODO Auto-generated method stubthis.successor=successor;}}
2 客户端以及要求
public class PurchaseRequest {private int Type = 0;private int Number = 0;private float Price = 0;private int ID = 0; public PurchaseRequest(int Type, int Number, float Price) {this.Type = Type;this.Number = Number;this.Price = Price;} public int GetType() {return Type;} public float GetSum() {return Number Price;} public int GetID() {return (int) (Math.random() 1000);}}public class Client { public Client() { } public PurchaseRequest sendRequst(int Type, int Number, float Price) {return new PurchaseRequest(Type, Number, Price);} }
3 组长、部长。。。继续决策者抽象类
public class GroupApprover extends Approver { public GroupApprover(String Name) {super(Name + " GroupLeader");// TODO Auto-generated constructor stub } @Overridepublic void ProcessRequest(PurchaseRequest request) {// TODO Auto-generated method stub if (request.GetSum() < 5000) {System.out.println("This request " + request.GetID() + " will be handled by " + this.Name + " ");} else {successor.ProcessRequest(request);}} }public class DepartmentApprover extends Approver { public DepartmentApprover(String Name) {super(Name + " DepartmentLeader"); } @Overridepublic void ProcessRequest(PurchaseRequest request) {// TODO Auto-generated method stub if ((5000 <= request.GetSum()) && (request.GetSum() < 10000)) {System.out.println("This request " + request.GetID()+ " will be handled by " + this.Name + " ");} else {successor.ProcessRequest(request);} } }
4测试
public class MainTest { public static void main(String[] args) { Client mClient = new Client();Approver GroupLeader = new GroupApprover("Tom");Approver DepartmentLeader = new DepartmentApprover("Jerry");Approver VicePresident = new VicePresidentApprover("Kate");Approver President = new PresidentApprover("Bush"); GroupLeader.SetSuccessor(VicePresident);DepartmentLeader.SetSuccessor(President);VicePresident.SetSuccessor(DepartmentLeader);President.SetSuccessor(GroupLeader); GroupLeader.ProcessRequest(mClient.sendRequst(1, 10000, 40)); } }
17 命令模式
定义:将一个要求封装为一个工具,使发出要求的任务和实行要求的任务分割开。这样两者之间通过命令工具进行沟通,这样方便将命令工具进行储存、通报、调用、增加与管理。
意图:将一个要求封装成一个工具,从而使您可以用不同的要求对客户进行参数化。
紧张办理:在软件系统中,行为要求者与行为实现者常日是一种紧耦合的关系,但某些场合,比如须要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变革的紧耦合的设计就不太得当。
何时利用:在某些场合,比如要对行为进行"记录、撤销/重做、事务"等处理,这种无法抵御变革的紧耦合是不得当的。在这种情形下,如何将"行为要求者"与"行为实现者"解耦?将一组行为抽象为工具,可以实现二者之间的松耦合。
如何办理:通过调用者调用接管者实行命令,顺序:调用者→接管者→命令。
17.1模式构造和代码示例
抽象命令类(Command)角色:声明实行命令的接口,拥有实行命令的抽象方法 execute()。详细命令角色(Concrete Command)角色:是抽象命令类的详细实现类,它拥有吸收者工具,并通过调用吸收者的功能来完成命令要实行的操作。实现者/吸收者(Receiver)角色:实行命令功能的干系操作,是详细命令工具业务的真正实现者。调用者/要求者(Invoker)角色:是要求的发送者,它常日拥有很多的命令工具,并通过访问命令工具来实行干系要求,它不直接访问吸收者。
代码举例(开灯和关灯),类图如下:
1 命令抽象类
public interface Command {public void excute();public void undo(); }
2 详细命令工具
public class TurnOffLight implements Command { private Light light; public TurnOffLight(Light light) {this.light = light;} @Overridepublic void excute() {// TODO Auto-generated method stublight.Off();} @Overridepublic void undo() {// TODO Auto-generated method stublight.On();} }
3 实现者
public class Light { String loc = ""; public Light(String loc) {this.loc = loc;} public void On() { System.out.println(loc + " On");} public void Off() { System.out.println(loc + " Off");} }
4 要求者
public class Contral{ public void CommandExcute(Command command) {// TODO Auto-generated method stubcommand.excute();} public void CommandUndo(Command command) {// TODO Auto-generated method stubcommand.undo();} }
18 状态模式
定义: 在状态模式中,我们创建表示各种状态的工具和一个行为随着状态工具改变而改变的 context 工具。
大略理解,一个拥有状态的context工具,在不同的状态下,其行为会发生改变。
意图:许可工具在内部状态发生改变时改变它的行为,工具看起来彷佛修正了它的类。
紧张办理:工具的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的干系行为。
何时利用:代码中包含大量与工具状态有关的条件语句。
如何办理:将各种详细的状态类抽象出来。
关键代码:常日命令模式的接口中只有一个方法。而状态模式的接口中有一个或者多个方法。而且,状态模式的实现类的方法,一样平常返回值,或者是改变实例变量的值。也便是说,状态模式一样平常和工具的状态有关。实现类的方法有不同的功能,覆盖接口中的方法。状态模式和命令模式一样,也可以用于肃清 if...else 等条件选择语句。
优点: 1、封装了转换规则。 2、列举可能的状态,在列举状态之前须要确定状态种类。 3、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只须要改变工具状态即可改变工具的行为。 4、许可状态转换逻辑与状态工具合成一体,而不是某一个巨大的条件语句块。 5、可以让多个环境工具共享一个状态工具,从而减少系统中工具的个数。
缺陷: 1、状态模式的利用一定会增加系统类和工具的个数。 2、状态模式的构造与实现都较为繁芜,如果利用不当将导致程序构造和代码的混乱。 3、状态模式对"开闭原则"的支持并不太好,对付可以切换状态的状态模式,增加新的状态类须要修正那些卖力状态转换的源代码,否则无法切换到新增状态,而且修正某个状态类的行为也需修正对应类的源代码。
18.1 模式构造和代码示例
State抽象状态角色
接口或抽象类,卖力工具状态定义,并且封装环境角色以实现状态切换。
ConcreteState详细状态角色详细状态紧张有两个职责:一是处理本状态下的事情,二是从本状态如何过渡到其他状态。
Context环境角色定义客户端须要的接口,并且卖力详细状态的切换。
举例(人物在地点A向地点B移动,在地点B向地点A移动)。类图如下:
1 state接口
public interface State {public void stop();public void move(); }
2 状态实例
public class PlaceA implements State { private Player context; public PlaceA(Player context) {this.context = context;} @Overridepublic void move() {System.out.println("处于地点A,开始向B移动");System.out.println("--------");context.setDirection("AB");context.setState(context.onMove); } @Overridepublic void stop() {// TODO Auto-generated method stubSystem.out.println("正处在地点A,不用停滞移动");System.out.println("--------");} }
3 context(player)拥有状态的工具
public class Player { State placeA;State placeB;State onMove;private State state;private String direction; public Player() {direction = "AB";placeA = new PlaceA(this);placeB = new PlaceB(this);onMove = new OnMove(this);this.state = placeA;} public void move() {System.out.println("指令:开始移动");state.move();} public void stop() {System.out.println("指令:停滞移动");state.stop();} public State getState() {return state;} public void setState(State state) {this.state = state;} public void setDirection(String direction) {this.direction = direction;} public String getDirection() {return direction;} }
19 备忘录模式
定义: 在不毁坏封装性的条件下,捕获一个工具的内部状态,并在该工具之外保存这个状态,以便往后当须要时能将该工具规复到原来保存的状态。该模式又叫快照模式。
备忘录模式是一种工具行为型模式,其紧张优点如下。
供应了一种可以规复状态的机制。当用户须要时能够比较方便地将数据规复到某个历史的状态。实现了内部状态的封装。除了创建它的发起人之外,其他工具都不能够访问这些状态信息。简化了发起人类。发起人不须要管理和保存其内部状态的各个备份,所有状态信息都保存在备忘录中,并由管理者进行管理,这符合单一职责原则。其紧张缺陷是:资源花费大。如果要保存的内部状态信息过多或者特殊频繁,将会占用比较大的内存资源。
19.1 模式构造图和代码示例
发起人(Originator)角色:记录当前时候的内部状态信息,供应创建备忘录和规复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。备忘录(Memento)角色:卖力存储发起人的内部状态,在须要的时候供应这些内部状态给发起人。管理者(Caretaker)角色:对备忘录进行管理,供应保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修正。
举例(发起者通过备忘录存储信息和获取信息),类图如下:
1 备忘录接口
public interface MementoIF { }
2 备忘录
public class Memento implements MementoIF{private String state; public Memento(String state) {this.state = state;}public String getState(){return state;} }
3 发起者
public class Originator { private String state; public String getState() {return state;} public void setState(String state) {this.state = state;} public Memento saveToMemento() {return new Memento(state);} public String getStateFromMemento(MementoIF memento) {return ((Memento) memento).getState();} }
4 管理者
public class CareTaker {private List<MementoIF> mementoList = new ArrayList<MementoIF>(); public void add(MementoIF memento) {mementoList.add(memento);} public MementoIF get(int index) {return mementoList.get(index);} }
20 访问者模式
定义:将浸染于某种数据构造中的各元素的操作分离出来封装成独立的类,使其在不改变数据构造的条件下可以添加浸染于这些元素的新的操作,为数据构造中的每个元素供应多种访问办法。它将对数据的操作与数据构造进行分离。
访问者(Visitor)模式是一种工具行为型模式,其紧张优点如下。
扩展性好。能够在不修正工具构造中的元素的情形下,为工具构造中的元素添加新的功能。复用性好。可以通过访问者来定义全体工具构造通用的功能,从而提高系统的复用程度。灵巧性好。访问者模式将数据构造与浸染于构造上的操作解耦,使得操作凑集可相对自由地蜕变而不影响系统的数据构造。符合单一职责原则。访问者模式把干系的行为封装在一起,构成一个访问者,使每一个访问者的功能都比较单一。访问者(Visitor)模式的紧张缺陷如下。
增加新的元素类很困难。在访问者模式中,每增加一个新的元素类,都要在每一个详细访问者类中增加相应的详细操作,这违背了“开闭原则”。毁坏封装。访问者模式中详细元素对访问者公布细节,这毁坏了工具的封装性。违反了依赖颠倒原则。访问者模式依赖了详细类,而没有依赖抽象类。20.1 模式构造和代码示例
访问者模式包含以下紧张角色。
抽象访问者(Visitor)角色:定义一个访问详细元素的接口,为每个详细元素类对应一个访问操作 visit() ,该操作中的参数类型标识了被访问的详细元素。详细访问者(ConcreteVisitor)角色:实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么。抽象元素(Element)角色:声明一个包含接管操作 accept() 的接口,被接管的访问者工具作为 accept() 方法的参数。详细元素(ConcreteElement)角色:实现抽象元素角色供应的 accept() 操作,其方法体常日都是 visitor.visit(this) ,其余详细元素中可能还包含本身业务逻辑的干系操作。工具构造(Object Structure)角色:是一个包含元素角色的容器,供应让访问者工具遍历容器中的所有元素的方法,常日由 List、Set、Map 等聚合类实现。1 抽象访问者
public interface Visitor { abstract public void Visit(Element element);}
2 详细访问者
public class CompensationVisitor implements Visitor { @Overridepublic void Visit(Element element) {// TODO Auto-generated method stubEmployee employee = ((Employee) element); System.out.println(employee.getName() + "'s Compensation is " + (employee.getDegree() employee.getVacationDays() 10));} }
3 抽象元素
public interface Element {abstract public void Accept(Visitor visitor); }
4 详细元素
public class CompensationVisitor implements Visitor { @Overridepublic void Visit(Element element) {// TODO Auto-generated method stubEmployee employee = ((Employee) element); System.out.println(employee.getName() + "'s Compensation is " + (employee.getDegree() employee.getVacationDays() 10));} }
5 工具构造
public class ObjectStructure {private HashMap<String, Employee> employees; public ObjectStructure() {employees = new HashMap();} public void Attach(Employee employee) {employees.put(employee.getName(), employee);} public void Detach(Employee employee) {employees.remove(employee);} public Employee getEmployee(String name) {return employees.get(name);} public void Accept(Visitor visitor) {for (Employee e : employees.values()) {e.Accept(visitor);}} }
21 中介者模式
定义:定义一个中介工具来封装一系列工具之间的交互,使原有工具之间的耦合疏松,且可以独立地改变它们之间的交互。中介者模式又叫调解模式,它是迪米特法则的范例运用。
中介者模式是一种工具行为型模式,其紧张优点如下。
降落了工具之间的耦合性,使得工具易于独立地被复用。将工具间的一对多关联转变为一对一的关联,提高系统的灵巧性,使得系统易于掩护和扩展。其紧张缺陷是:当同事类太多时,中介者的职责将很大,它会变得繁芜而弘大,以至于系统难以掩护。
21.1 模式构造和代码示例
抽象中介者(Mediator)角色:它是中介者的接口,供应了同事工具注册与转发同事工具信息的抽象方法。详细中介者(ConcreteMediator)角色:实现中介者接口,定义一个 List 来管理同事工具,折衷各个同事角色之间的交互关系,因此它依赖于同事角色。抽象同事类(Colleague)角色:定义同事类的接口,保存中介者工具,供应同事工具交互的抽象方法,实现所有相互影响的同事类的公共功能。详细同事类(Concrete Colleague)角色:是抽象同事类的实现者,当须要与其他同事工具交互时,由中介者工具卖力后续的交互。
举例(通过中介卖方),类图如下:
1 抽象中介者
public interface Mediator { void register(Colleague colleague); // 客户注册 void relay(String from, String to,String ad); // 转发 }
2 详细中介者
public class ConcreteMediator implements Mediator { private List<Colleague> colleagues = new ArrayList<Colleague>(); @Overridepublic void register(Colleague colleague) {// TODO Auto-generated method stubif (!colleagues.contains(colleague)) {colleagues.add(colleague);colleague.setMedium(this);}} @Overridepublic void relay(String from, String to, String ad) {// TODO Auto-generated method stubfor (Colleague cl : colleagues) { String name = cl.getName();if (name.equals(to)) {cl.receive(from, ad);} } } }
3 抽象同事类
public abstract class Colleague { protected Mediator mediator;protected String name; public Colleague(String name) {this.name = name;} public void setMedium(Mediator mediator) { this.mediator = mediator; } public String getName() {return name;} public abstract void Send(String to, String ad); public abstract void receive(String from, String ad); }
4 详细同事类
public class Buyer extends Colleague { public Buyer(String name) { super(name); } @Overridepublic void Send(String to, String ad) {// TODO Auto-generated method stubmediator.relay(name, to, ad);} @Overridepublic void receive(String from, String ad) {// TODO Auto-generated method stubSystem.out.println(name + "吸收到来自" + from + "的:" + ad);} }