JAVA设计模式
设计模式原则:
开闭原则:当需求有变化时,不修改原代码也可扩展新功能。
里氏替换原则:继承必须确保超类所拥有的性质在子类中仍然成立,主要阐述了有关继承的一些原则,也就是什么时候应该使用继承,什么时候不应该使用继承,
以及其中蕴含的原理。里氏替换原是继承复用的基础,它反映了基类与子类之间的关系,是对开闭原则的补充,是对实现抽象化的具体步骤的规范
依赖倒置原则:高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。其核心思想是:要面向接口编程,不要面向实现编程。
单一责任原则:单一职责原则的核心就是控制类的粒度大小、将对象解耦、提高其内聚性
接口隔离原则:要求程序员尽量将臃肿庞大的接口拆分成更小的和更具体的接口,让接口中只包含客户感兴趣的方法
迪米特原则:只与你的直接朋友交谈,不跟“陌生人”说话(Talk only to your immediate friends and not to strangers)。其含义是:如果两个软件实体无须直接通信,
那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性
合成复用原则:又叫组合/聚合复用原则(Composition/Aggregate Reuse Principle,CARP)。它要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,
其次才考虑使用继承关系来实现。如果要使用继承关系,则必须严格遵循里氏替换原则。合成复用原则同里氏替换原则相辅相成的,两者都是开闭原则的具体实现规范
三大分类:
创建型:对象实例化模式,用于解耦对象之间的耦合
结构型:把对象封装成更大的结构
行为型:类和对象之间交互
模式总览:
分类模式概要
分类 | 模式 | 概要 |
创建型 | 单例(Singleton)模式 |
该类只允许创建一个实例(五种方式) 1、懒汉;2、饿汉;3、多重检查锁;4、枚举;5、内部类 |
工厂(Factory Method)模式 | 通过工厂类获取想要的指定实例 | |
抽象工厂(AbstractFactory)模式 | 提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。 | |
建造者(Builder)模式 | 将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。 | |
原型(Prototype)模式 | 将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。 | |
结构型 | 代理(Proxy)模式 |
为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。 1、静态代理; 2、动态代理(jdk\cglib) |
适配器(Adapter)模式 | 将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作 | |
桥接(Bridge)模式 | 将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。 | |
装饰(Decorator)模式 |
动态的给对象增加一些职责,即增加其额外的功能 是继承关系的一种替代方案 |
|
外观/门面(Facade)模式 |
为多个复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问 Controller就属于外观模式 |
|
享元(Flyweight)模式 | 运用共享技术来有效地支持大量细粒度对象的复用 | |
组合(Composite)模式 | 将对象组合成树状层次结构,使用户对单个对象和组合对象具有一致的访问性 | |
行为型 | 模板方法(TemplateMethod)模式 | 定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤 |
策略(Strategy)模式 | 定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户 | |
命令(Command)模式 | 将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开 | |
职责链(Chain of Responsibility)模式 | 把请求从链中的一个对象传到下一个对象,直到请求被响应为止。通过这种方式去除对象之间的耦合 | |
状态(State)模式 | 允许一个对象在其内部状态发生改变时改变其行为能力 | |
观察者(Observer)模式 | 多个对象间存在一对多关系,当一个对象发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为 | |
中介者(Mediator)模式 | 定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解 | |
迭代器(Iterator)模式 | 提供一种方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示 | |
访问者(Visitor)模式 | 在不改变集合元素的前提下,为一个集合中的每个元素提供多种访问方式,即每个元素有多个访问者对象访问 | |
备忘录(Memento)模式 | 在不破坏封装性的前提下,获取并保存一个对象的内部状态,以便以后恢复它 | |
解释器(Interpreter)模式 | 提供如何定义语言的文法,以及对语言句子的解释方法,即解释器 |
详细介绍
单例模式:
场景:一般系统中通用的全局对象,例如保存配置参数。
实现:1、不允许外部调用类的构造函数,将构造函数私有化。 2、创建返回单例对象的静态方法。
优点:节省内存;避免频繁创建对象,提高性能;避免多对象共用资源,多线程问题;
缺点:对象长时间不被使用,会被回收
代码:
/** * 单例模式(饿汉) * 缺点:不管使用不使用该类,都会创建对象 */ public class SingletonBean2 { private static SingletonBean2 bean = new SingletonBean2(); private SingletonBean2(){} public static SingletonBean2 getBean(){ return bean; } }
/** * 单例模式(懒汉) * 缺点:多线程时存在并发问题 */ public class SingletonBean { private static SingletonBean bean; private SingletonBean(){} public static SingletonBean getBean(){ if (bean == null) { bean = new SingletonBean(); } return bean; } }
/** * 单例模式(同步锁机制) */ public class SingletonBean3 { private static SingletonBean3 bean; private SingletonBean3(){} public static SingletonBean3 getBean(){ if (bean == null) { synchronized (SingletonBean3.class) { bean = new SingletonBean3(); } } return bean; } }
原型模式:
类的复制(clone),不做过多说明
抽象工厂模式:
抽象工厂模式的主要角色如下。
- 抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法 newProduct(),可以创建多个不同等级的产品。
- 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
- 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
- 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。
建造者模式:
指将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式。
它是将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。它将变与不变相分离,即产品的组成部分是不变的,但每一部分是可以灵活选择的
由产品、抽象建造者、具体建造者、指挥者等 4 个要素构成
代码:
package com.example.design.bulider; /** * 卧室风格 */ public class Bedroom { private String color; private String style; public String getColor() { return color; } public void setColor(String color) { this.color = color; } public String getStyle() { return style; } public void setStyle(String style) { this.style = style; } @Override public String toString() { return "卧室装修风格{" + "颜色='" + color + '\'' + ", 风格='" + style + '\'' + '}'; } }
package com.example.design.bulider; /** * 客厅风格 */ public class Drawingroom { private String color; private String style; public String getColor() { return color; } public void setColor(String color) { this.color = color; } public String getStyle() { return style; } public void setStyle(String style) { this.style = style; } @Override public String toString() { return "客厅装修风格{" + "颜色='" + color + '\'' + ", 风格='" + style + '\'' + '}'; } }
package com.example.design.bulider; /** * 建造者模式 * 抽象建造者 */ public abstract class Builder { private House house = new House(); public abstract void decorateBedRoom(); public abstract void decorateDrawingRoom(); public House getHouse(){ return house; } }
package com.example.design.bulider; /** * 建造者模式 * 具体建造者(地中海风格) */ public class ConcreteBuilder extends Builder{ @Override public void decorateBedRoom() { Bedroom bedroom = new Bedroom(); bedroom.setColor("蓝色"); bedroom.setStyle("地中海"); super.getHouse().setBedroom(bedroom); } @Override public void decorateDrawingRoom() { Drawingroom room = new Drawingroom(); room.setColor("天蓝色"); room.setStyle("地中海"); super.getHouse().setDrawingroom(room); } }
package com.example.design.bulider; /** * 建造者模式 * 具体建造者(地中海风格) */ public class ConcreteBuilder2 extends Builder{ @Override public void decorateBedRoom() { Bedroom bedroom = new Bedroom(); bedroom.setColor("白色"); bedroom.setStyle("韩式"); super.getHouse().setBedroom(bedroom); } @Override public void decorateDrawingRoom() { Drawingroom room = new Drawingroom(); room.setColor("灰色"); room.setStyle("韩式"); super.getHouse().setDrawingroom(room); } }
package com.example.design.bulider; /** * 建造者模式 * 指挥者 * 调用建造者中的方法完成复杂对象的创建。 */ public class Director { private Builder builder; public Director(Builder builder) { this.builder = builder; } //产品构建与组装方法 public House construct() { builder.decorateBedRoom(); builder.decorateDrawingRoom(); return builder.getHouse(); } }
代理模式:
- 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
- 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
- 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
静态代理:
由程序员创建代理类或特定工具自动生成源代码再对其编译,在程序运行前代理类的 .class 文件就已经存在了。
package com.example.design.proxy; /** * 抽象主题 */ public interface Subject { void doSomeThing(); }
package com.example.design.proxy; /** * 抽象主题实现 */ public class SubjectImpl implements Subject{ @Override public void doSomeThing() { System.out.println("抽象主题实现方法"); } }
package com.example.design.proxy; public class Proxy implements Subject{ @Override public void doSomeThing() { System.out.println("代理开始"); Subject subject = new SubjectImpl(); subject.doSomeThing(); System.out.println("代理结束"); } }
动态代理:
在程序运行时,运用反射机制动态创建而成
package com.example.design.proxy.dynamic; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyFactory { Object target; public ProxyFactory(Object target) { this.target = target; } public Object getProxyInstance() { return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("动态代理开始"); Object invoke = method.invoke(target, args); System.out.println("动态代理结束"); return invoke; } }); } }
package com.example.design.proxy.dynamic; import com.example.design.proxy.Subject; import com.example.design.proxy.SubjectImpl; public class Test { public static void main(String[] args) { Subject subject = new SubjectImpl(); ProxyFactory proxyFactory = new ProxyFactory(subject); Subject proxyInstance = (Subject)proxyFactory.getProxyInstance(); proxyInstance.doSomeThing(); } }
适配器模式:
将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
桥接模式:
将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
举例说明:蜡笔和毛笔
package com.example.design.bridge.pen; public abstract class Pen { Color color; /** * 设置颜色 * @param color */ void setColor(Color color) { this.color = color; } /** * 画图 * @param val */ abstract void paint(String val); }
package com.example.design.bridge.pen; /** * 水彩颜色 */ public interface Color { /** * 涂颜色 */ void paint(); }
package com.example.design.bridge.pen; public class BigPen extends Pen{ /** * 画图 * * @param val */ @Override void paint(String val) { System.out.println("用大毛笔画"+val); color.paint(); } }
package com.example.design.bridge.pen; public class Blue implements Color { /** * 涂颜色 */ @Override public void paint() { System.out.println("开始喷墨:蓝色墨水卡卡喷"); } }
package com.example.design.bridge.pen; public class Red implements Color { /** * 涂颜色 */ @Override public void paint() { System.out.println("开始喷墨:红色墨水卡卡喷"); } }
package com.example.design.bridge.pen; public class Test { public static void main(String[] args) { Pen pen = new BigPen(); Color color = new Red(); pen.setColor(color); pen.paint("大太阳"); pen.setColor(new Blue()); pen.paint("天空"); } }
装饰器模式:
指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。
package com.example.design.decorator; /** * 猴子 */ public interface Monkey { void eat(); }
package com.example.design.decorator; /** * 孙悟空 */ public class Sunwukong implements Monkey { /** * 孙悟空吃香蕉 */ @Override public void eat() { System.out.println("孙悟空吃香蕉"); } }
package com.example.design.decorator; /** * 大猴子 */ public class BigMonkey implements Monkey { private Monkey monkey; public BigMonkey(Monkey monkey) { this.monkey = monkey; } @Override public void eat() { this.monkey.eat(); } }
package com.example.design.decorator; public class Jingang extends BigMonkey { public Jingang(Monkey monkey) { super(monkey); } public void eat() { super.eat(); eatExt(); } public void eatExt() { System.out.println("变成金刚,开始吃人"); } }
外观模式:
是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口,
外部应用程序不用关心内部子系统的具体细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。
在日常编码工作中,我们都在有意无意的大量使用外观模式。只要是高层模块需要调度多个子系统(2个以上的类对象),
我们都会自觉地创建一个新的类封装这些子系统,提供精简的接口,让高层模块可以更加容易地间接调用这些子系统的功能。
尤其是现阶段各种第三方SDK、开源类库,很大概率都会使用外观模式。
享元模式:
运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率
享元模式的主要优点是:相同对象只要保存一份,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。
其主要缺点是:
- 为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性。
- 读取享元模式的外部状态会使得运行时间稍微变长
组合模式:
有时又叫作整体-部分(Part-Whole)模式,它是一种将对象组合成树状的层次结构的模式,用来表示“整体-部分”的关系,使用户对单个对象和组合对象具有一致的访问性,属于结构型设计模式。
组合模式其实就是 类做为另外一个类的属性存在
模板方法模式:
定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。它是一种类行为型模式。
该模式的主要优点如下。
- 它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
- 它在父类中提取了公共的部分代码,便于代码复用。
- 部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。
该模式的主要缺点如下。
- 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象,间接地增加了系统实现的复杂度。
- 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。
- 由于继承关系自身的缺点,如果父类添加新的抽象方法,则所有子类都要改一遍。
package com.example.design.template; /** * 抽象模板类 */ public abstract class AbstractTemplate { /** * 执行模板方法 */ public void runTemplateMethco(){ //安排好执行顺序 method1(); method2(); method3(); } public void method1(){ System.out.println("父类执行方法1"); } /** * 抽象执行方法2 */ public abstract void method2() ; /** * 抽象执行方法3 */ public abstract void method3() ; }
package com.example.design.template; /** * 模板实现类 */ public class ConcreteTemplate extends AbstractTemplate{ /** * 抽象执行方法2 */ @Override public void method2() { System.out.println("实现类执行方法2"); } /** * 抽象执行方法3 */ @Override public void method3() { System.out.println("实现类执行方法3"); } }
策略模式:
该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。
策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。
策略模式的主要优点如下。
- 多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句,如 if...else 语句、switch...case 语句。
- 策略模式提供了一系列的可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类里面,从而避免重复的代码。
- 策略模式可以提供相同行为的不同实现,客户可以根据不同时间或空间要求选择不同的。
- 策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法。
- 策略模式把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现了二者的分离。
其主要缺点如下。
- 客户端必须理解所有策略算法的区别,以便适时选择恰当的算法类。
- 策略模式造成很多的策略类,增加维护难度。
package com.example.design.strategy; public interface BusiService { void exe(); }
package com.example.design.strategy; public class BusiServiceImpl1 implements BusiService { @Override public void exe() { System.out.println("实现方法1"); } }
package com.example.design.strategy; public class BusiServiceImpl2 implements BusiService { @Override public void exe() { System.out.println("实现方法2"); } }
package com.example.design.strategy; /** * 环境类 */ public class Context { BusiService busiService; public BusiService getBusiService() { return busiService; } public void setBusiService(BusiService busiService) { this.busiService = busiService; } public void exe(){ this.busiService.exe(); } }
package com.example.design.strategy; public class Test { public static void main(String[] args) { Context context = new Context(); BusiService busiService = new BusiServiceImpl1(); context.setBusiService(busiService); context.exe(); busiService = new BusiServiceImpl2(); context.setBusiService(busiService); context.exe(); } }
责任链模式:
为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;
当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
package com.example.design.chainOfResponsibility; /** * 责任抽象类 */ public abstract class Handler { Handler next; public Handler getNext() { return next; } public void setNext(Handler next) { this.next = next; } /** * 处理节点 */ abstract void run(String param); }
package com.example.design.chainOfResponsibility; public class HandlerFirst extends Handler { /** * 处理节点 */ @Override void run(String param) { if("one".equals(param)) { System.out.println("第一节点就处理完了!"); return; }else{ if (null == this.getNext()) { System.out.println("没有合适的节点处理该请求!"); return; } this.getNext().run(param); } } }
package com.example.design.chainOfResponsibility; public class HandlerSecond extends Handler { /** * 处理节点 */ @Override void run(String param) { if("two".equals(param)) { System.out.println("第二节点处理!"); return; }else{ if (null == this.getNext()) { System.out.println("没有合适的节点处理该请求!"); return; } this.getNext().run(param); } } }
package com.example.design.chainOfResponsibility; public class Test { public static void main(String[] args) { Handler handler = new HandlerFirst(); Handler handler1 = new HandlerSecond(); handler.setNext(handler1); handler.run("two1"); } }
状态模式:
对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
状态模式是一种对象行为型模式,其主要优点如下。
- 结构清晰,状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“单一职责原则”。
- 将状态转换显示化,减少对象间的相互依赖。将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖。
- 状态类职责明确,有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。
状态模式的主要缺点如下。
- 状态模式的使用必然会增加系统的类与对象的个数。
- 状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混乱。
- 状态模式对开闭原则的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源码,否则无法切换到新增状态,而且修改某个状态类的行为也需要修改对应类的源码。
观察者模式:
指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。
观察者模式是一种对象行为型模式,其主要优点如下。
- 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。符合依赖倒置原则。
- 目标与观察者之间建立了一套触发机制。
它的主要缺点如下。
- 目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
- 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率
package com.example.design.observer; import java.util.ArrayList; import java.util.List; /** * 抽象目标 */ public abstract class Subject { //观察者列表 List<Observer> observerList = new ArrayList<>(); /** * 添加观察者 * @param observer */ public void add(Observer observer) { this.observerList.add(observer); } /** * 删除观察者 * @param observer */ public void del(Observer observer) { this.observerList.remove(observer); } /** * 通知 */ abstract void send(); }
package com.example.design.observer; public class SubjectImpl extends Subject { /** * 通知 */ @Override void send() { System.out.println("通知观察者"); for (Observer observer : observerList) { observer.receiveMessages("赶紧干活!!!"); } } }
package com.example.design.observer; /** * 观察者接口 */ public interface Observer { /** * 接收消息接口 * @param val */ void receiveMessages(String val); }
package com.example.design.observer; public class ObserverImpl1 implements Observer { /** * 接收消息接口 * * @param val */ @Override public void receiveMessages(String val) { System.out.println("观察者收到消息:"+val); System.out.println("我就不干!!!"); } }
package com.example.design.observer; public class ObserverImpl2 implements Observer { /** * 接收消息接口 * * @param val */ @Override public void receiveMessages(String val) { System.out.println("观察者收到消息:"+val); System.out.println("收到,这就开始!!!"); } }
中介模式:
与观察者模式类似