本文最后更新于:4 个月前
设计模式原则 设计原则核心思想:
找出应用中可能需要变化之处,把他们独立出来,不要和那些不需要变化的代码放在一起。
针对接口编程,而不是针对实现编程。
为了交互对象之间的松耦合设计努力
1 2 3 4 5 6 7 8 设计模式常见的七大原则: 1)单一职责原则 2)接口隔离原则 3)依赖倒置原则 4)里式替换原则 5)开闭原则 6)迪米特原则 7)合成复用原则
代码重用性
可读性
可扩展性
可靠性
高内聚低耦合
单一隔离 1 2 3 4 原则上 一个类应该尽量做一件事 一个类继承一个接口 如果实现类两个职责 当职责一进行修改的时候 很可能回对职责二造成影响 但是一个类继承一个接口会导致开销过大 在接口方法比较少的情况下可以 通过向下兼容 实现方法的单一职责
接口隔离 1 2 如果一个接口方法过多,实现该接口就会需要去实现很多不需要实现的方法。 这个时候我们就应该把接口进行拆分,去实现需要实现的接口即可。
依赖倒转(倒置) 1 2 3 4 5 6 7 8 9 10 11 接口和抽象类的价值在于 设计 高层模块不应该依赖于底层模块 抽象不应该依赖细节,细节应该依赖抽象 面向接口编程 传递的三种方式1. 构造器传递2. set 接口传递3. 接口传递 使用接口或者抽象类的目的是制定好规范。而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成 多了一个缓冲利于程序的扩展和优化
里式替换原则 1 2 3 4 5 6 7 8 问题:在编程中如何正确的实现继承 尽量满足里式替换原则 子类尽量不要重写父类的方法 做到透明使用 如果子类想使用父类的方法 ,但是有可能会不小心重写了父类的方法 倒是一系列应用 带来了程序的入侵性 所以可以 子类和父类都继承一个新的base 类,base 类实现了更为基础的代码和方法 这样子类可以放心的重写方法 达到的效果是 所有应用基类的类应该尽量做到透明使用
开闭原则 1 2 3 4 5 6 7 8 9 10 11 12 开闭原则是编程中 最基础最重要的原则 一个软件实体类 模块和函数应该对外扩展开放(对提供方) 对修改关闭(对使用方) 用抽象构建,用实现扩展细节 当我们增加一个功能时候 应该增加代码而不是修改代码 尽量不去修改原有的代码 当软件需要变化时 尽量通过扩展软件实体的行为来实现变化 而不是通过修改已有的代码来实现变化 编程中遵循其他原则以及使用设计模式的目的就是遵循开闭原则 改进思路分析 把创建的Shape 类做成抽象类或者接口,并提供一个抽象的draw方法或者接口,让子类去实现即可。 这样有新的图形种类时候 只需要让新的图形去继承Shape 并且实现draw方法即可,这样使用方的代码就不需要修改 满足了开闭原则
迪米特法则 1 2 3 4 5 一个对象应该对其他的对象保持最少的了解 类与类之间的关系越密切,耦合度越大 一个类里面 除了传递参数依赖类 应该尽量避免出现其他的陌生类,降低耦合度 这样代码修改起来容易
合成服用原则 1 2 3 4 5 6 原则是尽量使用合成/聚合 而不是使用继承 B要想使用A 的方法,可以继承于A 但是这样会导致关系太强 耦合度太高 组合: 让B 里面注入一个A 聚合: 让B里面 set 一个A 或者构造器 依赖: 在B里面把A 传进来 称之为B依赖A 方法
设计模式类型 1 2 3 4 5 设计模式分为三种类型,共23种 1、创建型模式:单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式 2、适配器模式:桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式 3、行为型模式:模板方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、 备忘录模式、解释器模式、状态模式、策略模式、责任链模式
创建型模式 单例模式 饿汉式(静态常量) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Singleton { private Singleton () { } private final static Singleton instance = new Singleton(); public static Singleton getInstance () { return instance; } }
饿汉式(静态代码块) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Singleton { private Singleton () {} private static Singleton instance ; static { instance = new Singleton(); } public static Singleton getInstance () { return instance; } }
懒汉式(线程不安全) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Singleton { private static Singleton instance; private Singleton () {} public static Singleton getInstance () { if (instance == null ){ instance = new Singleton(); } return instance; } }
懒汉式(线程安全) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Singleton { private static Singleton instance; private Singleton () {} public static synchronized Singleton getInstance () { if (instance == null ){ instance = new Singleton(); } return instance; } }
双重检查(推荐) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Singleton { private static volatile Singleton singleton; private Singleton () {} public static Singleton getInstance () { if (singleton == null ){ synchronized (Singleton.class){ if (singleton == null ){ singleton = new Singleton(); } } } return singleton; } }
静态内部类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Singleton { private static volatile Singleton instance; private Singleton () {} private static class SingletonInstance { private static final Singleton INSTANCE = new Singleton(); } public static synchronized Singleton getInstance () { return SingletonInstance.INSTANCE; } }
枚举 1 2 3 4 5 6 7 8 9 10 11 12 13 enum Singleton { INSTANCE; public void sayOK () { System.out.println("ok~~~" ); } }
单例模式有八种方式:
1 2 3 4 5 6 7 8 饿汉式 饿汉式 懒汉式 懒汉式 懒汉式 并不能实现线程安全 双重检查 静态内部类 枚举
单例模式注意事项和细节说明:
1)单例模式保证了系统内存中 该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可提高系统的性能 2)当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是直接使用new 3)单例模式使用的场景:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或者耗费资源过多(即重量级对象),但又经常用到的对象,工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂等)
1 2 3 如果确定实例一定会使用 饿汉式是可以使用的 只是有可能会造成内存浪费 比如java的Runtime中就用了饿汉式 推荐使用: 双重检查、静态内部类、枚举
工厂模式 简单工厂模式 需求:一个披萨项目,要便于披萨种类的扩展,要便于维护
披萨的种类有很多(比如GreekPizz
、Chjeesepizz
等)
披萨的制作有prepare,bake,cut,box
完成披萨店订购功能。
工厂方法模式 抽象工厂模式
抽象工厂模式:定义了一个interface用于创建相关或有依赖关系的对象 cu答族,而无需指明具体的类
抽象工厂模式可以将简单工厂模式和工厂方法进行整合
从设计层面看,抽象工厂模式就是对简单工厂模式的改进或者说进一步的抽象
将工厂抽象成两橙,AbsFactory
(抽象工厂)和具体实现的工厂子类,程序员可以根据创建对象类型使用对应的工厂子类。这样将单个简单地工厂变成了工厂cu答族,更利于代码的维护
1 2 定义一个抽象的工厂,然后定义实体工厂实现工厂方法。
Calendar中使用了工厂模式
工厂模式小结
工厂模式的意义:将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到和主项目的依赖关系的解耦。从而提高项目的扩展和维护性。
三种工厂模式(简单工厂模式,工厂方法模式,抽象工厂模式)
设计模式的依赖抽象原则
创建对象实例中,不要直接new类,而是把这个new类的动作放在一个工厂的方法中,并返回。有的书上说不要直接持有具体类的应用。
不要让类继承具体类,而是继承抽象类或者是实现interface(接口)
不要覆盖基类中已经实现的方法
原型模式 需求
现有一只羊,姓名为tom,年龄为1:颜色为白色,请编写程序创建和tom羊属性完全相同的10只羊
1 2 3 4 5 6 7 8 9 10 11 12 public class Sheep { private String name; private int age; private String color; } public static void main (String[] args) { Sheep sheep = new Sheep("tom" ,1 ,"白色" ); new Sheep(sheep.getName(),sheep.getAge(),sheep.getColor()); }
传统方式的优缺点
有点是比较好理解,简单易操作
在创建新的对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂,效率较低
总是需要重新初始化对象,而不是动态的获得对象运行时的状态,不够灵活
思路:java
中Object类是所有类的父类,Object提供了一个clone方法,可以将一个java
对象复制一份,但是需要实现clone的java
类需要实现一个接口Clonealbe
,该接口表示该类能够复制且具有复制的能力=>原型模式
基本介绍
原型模式(Prototype模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象
原型模式是一种创建型设计模式,允许一个对象在创建另外一个可定制的对象,无需知道如何创建的细节
工作的原理:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝他们来实施创建,即对象.clone()
形象理解:猴子拔出猴毛变成其他猴子
改进:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class Sheep implements Cloneable { private String name; private int age; private String color; @Override protected Object clone () throws CloneNotSupportedException { Sheep sheep = null ; Sheep clone = (Sheep) super .clone(); return clone; } }public class Client { public static void main (String[] args) { Sheep sheep = new Sheep("tom" ,1 ,"包塞" ); try { Sheep clone1 = (Sheep) sheep.clone(); Sheep clone2 = (Sheep) sheep.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } } }
原型模式在Spring框架中源码分析
1 2 3 4 5 6 public void testImport () { Object bean1 = applicationContext.getBean(xxx); Object bean2 = applicationContext.getBean(xxx); }
原型模式完成对象的创建,如果被克隆的对象中有对象属性,那么克隆的时候并不会被克隆
深拷贝和浅拷贝 浅拷贝:
对于数据类型是基本类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性复制一份给新的对象
对于数据类型是应用数据类型的成员变量,比如成员变量是某个数组,某个类的对象等,那么浅拷贝会进行引用传递,也就是只将该成员变量的引用值(内存地址)复制一份给新的对象。实际上两个对象都指向同一个实例,所以这种情况下 一个对象中修改该成员变量会影响到另一个对象的该成员变量值
比如默认开启的对象克隆就是浅拷贝
Sheep clone2 = (Sheep) sheep.clone();
深拷贝:
复制对象的所有 基本数据类型的成员变量值
为所有应用数据类型的成员变量申请存储空间 ,并复制每个应用数据类型成员变量所引用的对象,直到该对象可达的所有对象,也就是说,对象进行深拷贝要对整个对象(包括引用对象)进行拷贝
深拷贝实现方式1:重写clone 方法来实现深拷贝
深拷贝实现方式2:通过序列化 实现深拷贝
小案例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class DeepCloneTarget implements Serializable ,Cloneable { private static final long serialVersionUID = 1L ; private String cloneName; private String cloneClass; public DeepCloneTarget (String cloneName, String cloneClass) { this .cloneName = cloneName; this .cloneClass = cloneClass; } @Override protected Object clone () throws CloneNotSupportedException { return super .clone(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 public class DeepProtoType implements Serializable ,Cloneable { public String name; public DeepCloneTarget deepCloneTarget; @Override protected Object clone () throws CloneNotSupportedException { Object deep = null ; deep = super .clone(); DeepProtoType deepProtoType = (DeepProtoType) deep; deepProtoType.deepCloneTarget = (DeepCloneTarget) deepCloneTarget.clone(); return deepProtoType; } public Object deepClone () { ByteArrayOutputStream bos = null ; ObjectOutputStream oos = null ; ByteArrayInputStream bis = null ; ObjectInputStream ois = null ; try { bos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(bos); oos.writeObject(this ); bis = new ByteArrayInputStream(bos.toByteArray()); ois = new ObjectInputStream(bis); DeepProtoType copy = (DeepProtoType) ois.readObject(); return copy; } catch (Exception e) { e.printStackTrace(); return null ; }finally { try { bos.close(); oos.close(); bis.close(); ois.close(); } catch (IOException e) { e.printStackTrace(); } } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class Client { public static void main (String[] args) throws CloneNotSupportedException { DeepProtoType dp = new DeepProtoType(); dp.name = "小水牛" ; dp.deepCloneTarget = new DeepCloneTarget("大水牛" , "敲能喝" ); DeepProtoType p = (DeepProtoType) dp.deepClone(); System.out.println("克隆原型:" +dp.name + "--" + dp.deepCloneTarget.hashCode()); System.out.println("克隆的对象" +p.name + "--" + p.deepCloneTarget.hashCode()); } }
小结
创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率
不用重新初始化对象,而是动态的获得对象运行时的状态
如果原型对象发生变化(增加或减少属性),其他克隆对象也会发生相应的变化,无需修改代码
在实现深克隆的时候 可能需要比较复杂的代码
缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但是对已有的类来说进行改造的时候 需要修改它的源代码,违背了OCP
(开闭原则)。
建造者模式
需求:需要建房子 这一过程为打桩 砌墙 封顶
房子有各种各种的 比如普通房 高楼 别墅 各种房子的过程虽然一样 但是要求不要相同
编写程序完成需求
案例分析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public abstract class AbstractHouse { public abstract void buildBasic () ; public abstract void buildWalls () ; public abstract void roofed () ; public void build () { buildBasic(); buildWalls(); roofed(); } }public class CommonHouse extends AbstractHouse { @Override public void buildBasic () { System.out.println("给普通房子打地基" ); } @Override public void buildWalls () { System.out.println("给普通房子砌墙" ); } @Override public void roofed () { System.out.println("给普通房子封顶" ); } }public class Client { public static void main (String[] args) { CommonHouse commonHouse = new CommonHouse(); commonHouse.build(); } }
有点是比较好理解,简单易操作
设计的程序结构过于简单,没有设计缓存层对象,程序的扩展和维护不好,也就是说 这种设计方案把产品(房子)和创建产品的过程(建房子build方法)封装在一起 ,耦合性增强了
解决方案:将产品 和产品建造过程 解耦 => 建造者模式
基本介绍
建造者模式(Builder Pattern)又叫生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别) 使这个抽象过程的不同方法可以构造出不同表现(属性)的对象。
建造者模式是一步一步创建一个复杂的对象 ,他允许用户指定复杂的类型就可以构建他们,用户不需要知道具体构建细节。
建造者模式的四个角色
Product(产品角色) :一个具体的产品对象
Builder(抽象建造者):创建一个Product对象的各个部件的接口/抽象类
ConcreteBuilder
(具体建造者)实现接口,构建和装配各个部件
Director(指挥者):构建一个Builder接口的对象,它主要是用于创建一个复杂的对象。主要有两个作用,一是:隔离客户与对象产生的过程,二是:复杂控制产品对象的生产过程
产品:
1 2 3 4 5 6 7 public class House { private String base; private String wall; private String roofed; }
抽象的建造者Builder:
1 2 3 4 5 6 7 8 9 10 11 12 13 public abstract class HouseBuilder { protected House house = new House(); public abstract void buildBasic () ; public abstract void buildWalls () ; public abstract void roofed () ; public House BuildHouse () { return house; } }
指挥者Director
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class HouseDirector { HouseBuilder houseBuilder = null ; public HouseDirector (HouseBuilder houseBuilder) { this .houseBuilder = houseBuilder; } public void setHouseBuilder (HouseBuilder houseBuilder) { this .houseBuilder = houseBuilder; } public House constructorHouse () { houseBuilder.buildBasic(); houseBuilder.buildWalls(); houseBuilder.roofed(); return houseBuilder.BuildHouse(); } }
两个产品类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class CommonHouse extends HouseBuilder { @Override public void buildBasic () { System.out.println("普通房子打地基五米" ); } @Override public void buildWalls () { System.out.println("普通房子砌墙10cm" ); } @Override public void roofed () { System.out.println("普通房子封顶" ); } }public class HighBuilding extends HouseBuilder { @Override public void buildBasic () { System.out.println("高楼打地基50" ); } @Override public void buildWalls () { System.out.println("高楼砌墙20cm" ); } @Override public void roofed () { System.out.println("高楼透明屋顶" ); } }
实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class Client { public static void main (String[] args) { CommonHouse commonHouse = new CommonHouse(); HouseDirector houseDirector = new HouseDirector(commonHouse); House house = houseDirector.constructorHouse(); HighBuilding highBuilding = new HighBuilding(); houseDirector.setHouseBuilder(highBuilding); House house1 = houseDirector.constructorHouse(); } }
JDK
中用到的建造者模式 java.lang.StringBuilder
Appendable
接口定义了多个append方法(抽象方法) , 即Appendable
为建造者,定义了抽象方法
AbstractStringBuilder
已经是建造者,只是不能实例化
StringBuilder
即充当了指挥者角色,同时同时充当了具体的建造者。建造方法的实现是由AbstractStringBuilder
完成,而StringBuilder
继承了AbstractStringBuilder
建造者模式的注意事项和细节
客户端(使用程序)不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
建造者模式所创建的产品一般具有较多相同的共同点,其组成部分相似,如果产品之间的差异性很大,则不是和使用建造者模式,因此其使用范围收到一定的限制。
如果产品内部变化复杂,可能会导致需要定义很多具体建造者来实现这种变化,导致系统变得庞大,因此要考虑是否适合选择建造者模式。
每一个具体的建造者都相对独立,而与其他的具体建造者无关,因此可以很方便的替换具体建造者或者增加新的具体建造者,用户使用不同的建造者即可得到不同的产品对象。
可以更加精细的控制产品的建造过程,将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建流程。
增加新的具体创建者无需修改原油类库的代码,指挥者针对抽象建造者类变成,系统扩展方便,符合开闭原则。
抽象工厂模式和建造者模式
抽象工厂模式实现对产品家族的创建,一个产品家族是一系列产品,具有不同分类维度的产品组合,采用抽象工厂模式不需要关心构建过程,只关心什么产品由什么工厂生产。而建造者模式则是按照指定的蓝图构建产品,它的主要目的是通过组装零配件而生产一个新的产品。
结构型模式 适配器模式 基本介绍
适配器模式(Adapter Pattern
)将某个类的接口转换成客户端期望的另一个接口表示,主要目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器(Wrapper)
适配器模式属于结构型模式
主要分为三类:类适配器模式,对象适配器模式 ,接口适配器模式
工作原理
适配器模式:将一个类的接口转换成另一种接口,让原本接口不兼容的类可以兼容
从用户的角度看不到适配者,是解耦的。
用户调用适配器转换出来的目标接口方法,适配器再调用被适配者的相关接口方法
用户收到反馈结果,只感觉是和目标接口交互
类适配器模式 1 2 3 4 5 6 7 8 9 10 public class Voltage220V { public int output220V () { int src = 220 ; System.out.println("电压=" + src + "V" ); return src; } }
1 2 3 4 public interface Voltage5V { public int output5V () ; }
1 2 3 4 5 6 7 8 9 10 11 12 public class VoltageAdapter extends Voltage220V implements Voltage5V { @Override public int output5V () { int srcV = output220V(); int destV = srcV/44 ; return destV; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 public class Phone { public void charging (Voltage5V iVoltage) { if (iVoltage.output5V() == 5 ){ System.out.println("电压为5V,可冲" ); }else if (iVoltage.output5V() > 5 ){ System.out.println("电压大于5V 无法充电" ); } } }
1 2 3 4 5 6 7 public class Client { public static void main (String[] args) { System.out.println("====类适配器模式" ); Phone phone = new Phone(); phone.charging(new VoltageAdapter()); } }
类适配器模式注意事项和细节
Java是单继承机制 ,所以类适配器需要继承src
这一点算是一个缺点,因为这要求dst
必须是接口,有一定的局限性
src
类的方法在Adapter中都会暴露出来,增加了使用成本
由于其继承了src
类,所以它可以根据需求重写src
类的方法,使得Adapter的灵活性增强了\
对象适配器 基本介绍
基本思路和类的适配器相同,只是将Adapter类做修改,不是继承src
类,而是持有src
的类型,以解决兼容问题。即:持有src
类,实现dst
类接口,完成src
->dst
的适配。
根据“合成复用原则”,在系统中尽量使用关联关系来代替继承关系。
对象适配器模式是适配器模式常用的一种
1 2 3 4 5 6 7 8 9 10 11 public class Voltage220V { public int output220V () { int src = 220 ; System.out.println("电压=" + src + "V" ); return src; } }
1 2 3 4 public interface Voltage5V { public int output5V () ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class VoltageAdapter implements Voltage5V { private Voltage220V voltage220; public VoltageAdapter (Voltage220V voltage220) { this .voltage220 = voltage220; } @Override public int output5V () { int destV = 0 ; if (null != voltage220){ int src = voltage220.output220V(); System.out.println("适配器适配" ); destV = src /44 ; System.out.println("适配完成,输出的电压为" + destV); } return destV; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 public class Phone { public void charging (Voltage5V iVoltage) { if (iVoltage.output5V() == 5 ){ System.out.println("电压为5V,可冲" ); }else if (iVoltage.output5V() > 5 ){ System.out.println("电压大于5V 无法充电" ); } } }
1 2 3 4 5 6 7 public class Client { public static void main (String[] args) { System.out.println("====对象适配器模式" ); Phone phone = new Phone(); phone.charging(new VoltageAdapter(new Voltage220V() )); } }
对象适配器模式注意事项和细节
对象适配器和类适配器算是同一种思想,只不过是实现方式不同。根据合成服用原则,使用组合替代继承,所以他解决了适配器必须继承src
的局限性问题,也不再要修dst
必须是接口。
使用成本更低,更灵活
接口适配器模式 基本介绍
一些书籍称为:适配器模式(Default Adapter Pattern)或缺省适配器模式。
当不需要全部实现接口提供的方法时,可以设计一个抽象类实现接口,并为该接口中每一个方法提供一个默认实现(空方法) ,那么该抽象类的子类可有选择的覆盖父类的某些方法实现需求。
适用于一个接口不想使用其所有的方法的情况。
源码分析
SpringMVC
中的HandlerAdapter
就使用到了适配器模式 很牛逼,但是现在看不懂,记得回来看~
小结
三种命名方式,是根据src
以怎样的形式给到Adapter(在Adapte
里的形式)来命名的。
三种适配器
类适配器:以类给到,在Adapter里,就是将src
当做类继承
对象适配器:以对象给到,在Adapter里,将src
作为一个对象持有
接口适配器:以接口给到,在Adapter里,将src
作为一个接口,实现
Adapter模式最大的作用还是将原本不兼容的接口融合在一起工作。
实际开发中 实现不拘泥于这三种经典形式
桥接模式 问题分析:类爆炸问题。
基本介绍
桥接模式(Bridge):将实现与抽象类在两个不同的类层次中,使两个层次可以独立改变
Bridge基于类的最小设计原则,通过使用继承,聚合,封装等不同方式来让不同的类承担不同的职责。它的主要特点是把抽象(Abstraction)和实现(Implementation)分离开,从而可以保证各个部分的独立性以及对他们的功能扩展
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public interface Brand { void open () ; void close () ; void call () ; }public class Vivo implements Brand { @Override public void open () { System.out.println(" Vivo手机开机 " ); } @Override public void close () { System.out.println(" Vivo手机关机 " ); } @Override public void call () { System.out.println(" Vivo手机打电话 " ); } }public class XiaoMi implements Brand { @Override public void open () { System.out.println(" 小米手机开机 " ); } @Override public void close () { System.out.println(" 小米手机关机 " ); } @Override public void call () { System.out.println(" 小米手机打电话 " ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 public abstract class Phone { private Brand brand; public Phone (Brand brand) {this .brand = brand;} protected void open () { this .brand.open(); } protected void close () { brand.close(); } protected void call () { brand.call(); } }public class UpRightPhone extends Phone { public UpRightPhone (Brand brand) { super (brand); } public void open () { super .open(); System.out.println(" 直立样式手机 " ); } public void close () { super .close(); System.out.println(" 直立样式手机 " ); } public void call () { super .call(); System.out.println(" 直立样式手机 " ); } }public class FoldedPhone extends Phone { public FoldedPhone (Brand brand) { super (brand); } public void open () { super .open(); System.out.println(" 折叠样式手机 " ); } public void close () { super .close(); System.out.println(" 折叠样式手机 " ); } public void call () { super .call(); System.out.println(" 折叠样式手机 " ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class Client { public static void main (String[] args) { Phone phone1 = new FoldedPhone(new XiaoMi()); phone1.open(); phone1.call(); phone1.close(); Phone phone2 = new FoldedPhone(new Vivo()); phone2.open(); phone2.call(); phone2.close(); UpRightPhone phone3 = new UpRightPhone(new XiaoMi()); phone3.open(); phone3.call(); phone3.close(); UpRightPhone phone4 = new UpRightPhone(new Vivo()); phone4.open(); phone4.call(); phone4.close(); } }
源码分析
JDBC
的Driver接口,从桥接模式来看,Driver就是一个接口,下面可以有Mysql
的Driver,也可以有Orical
的Driver,这些就可以当做实现接口类
小结
实现了抽象和实现类的分离,极大地提升了系统的灵活性,让抽象类和实现类部分独立开,有助于系统实现分层设计,从而产生更好的结构化系统。
对于系统的高层部分,只需要知道抽象部分和实现部分的接口即可,其他的部分有具体业务来完成。
桥接模式替代了多层继承方案,可以减少子类的个数,降低系统的管理和维护成本。
桥接模式的引入增加了系统的理解和设计难度,由于聚和关联关系建立在抽象层,要求开发者针对抽象进行设计和编程。
桥接模式要求正确识别出系统两个独立变化的维度,因此适用范围有一定的局限性。
常见的应用场景:
JDBC
驱动系统
银行转账系统
转账分类:网上转账,柜台转账,ATM转账
转账用户类型:普通用户,银卡用户,金卡用户…
消息管理
消息类型:即使消息,延时消息..
消息分类:短信,QQ
,微信..
装饰者模式
定义 :动态的将新功能附加到对象上 ,在对象功能扩展方面,比继承更有弹性,也体现了开闭原则(OCP
)
案例分析
星巴克咖啡订单项目(咖啡馆)
咖啡种类/单品咖啡:咖啡种类/单品咖啡:Espresso(意大利浓咖啡)、ShortBlack
、LongBlack
(美式咖啡)、Decaf(无因咖啡)
调料:Milk、Soy(豆浆)、Chocolate
要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便
使用OO
的来计算不同种类咖啡的费用:客户可以点单品咖啡,也可以单品咖啡+调料组合。
分析,一份单品咖啡可能有多份调料,而且单品咖啡和调料应该易于扩展,符合开闭原则。所以不能把单品咖啡和调料融合在一起,应该把咖啡当做被装饰者 ,调料当成装饰者 ,实现调料的具体子类为修饰着。图解图下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public abstract class Drink { public String des; private float price = 0.0f ; public abstract float cost () ; }public class Coffee extends Drink { @Override public float cost () { return super .getPrice(); } }public class DeCaf extends Coffee { public DeCaf () { setDes(" 无因咖啡 " ); setPrice(1.0f ); } }public class Espresso extends Coffee { public Espresso () { setDes(" 意大利咖啡 " ); setPrice(6.0f ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public class Decorator extends Drink { private Drink obj; public Decorator (Drink obj) { this .obj = obj; } @Override public float cost () { return super .getPrice() + obj.cost(); } @Override public String getDes () { return des + " " + getPrice() + " && " + obj.getDes(); } }public class Milk extends Decorator { public Milk (Drink obj) { super (obj); setDes(" 牛奶 " ); setPrice(2.0f ); } }public class Soy extends Decorator { public Soy (Drink obj) { super (obj); setDes(" 豆浆 " ); setPrice(1.5f ); } }public class Chocolate extends Decorator { public Chocolate (Drink obj) { super (obj); setDes(" 巧克力 " ); setPrice(3.0f ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public class CoffeeBar { public static void main (String[] args) { Drink order = new LongBlack(); System.out.println("费用1=" + order.cost()); System.out.println("描述=" + order.getDes()); order = new Milk(order); System.out.println("order 加入一份牛奶 费用 =" + order.cost()); System.out.println("order 加入一份牛奶 描述 = " + order.getDes()); order = new Chocolate(order); System.out.println("order 加入一份牛奶 加入一份巧克力 费用 =" + order.cost()); System.out.println("order 加入一份牛奶 加入一份巧克力 描述 = " + order.getDes()); order = new Chocolate(order); System.out.println("order 加入一份牛奶 加入2份巧克力 费用 =" + order.cost()); System.out.println("order 加入一份牛奶 加入2份巧克力 描述 = " + order.getDes()); System.out.println("===========================" ); Drink order2 = new DeCaf(); System.out.println("order2 无因咖啡 费用 =" + order2.cost()); System.out.println("order2 无因咖啡 描述 = " + order2.getDes()); order2 = new Milk(order2); System.out.println("order2 无因咖啡 加入一份牛奶 费用 =" + order2.cost()); System.out.println("order2 无因咖啡 加入一份牛奶 描述 = " + order2.getDes()); } }
源码分析
Java的IO结构,FilterInputStream
就是一个装饰者
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class DecoratorToJDKIO { public static void main (String[] args) throws Exception { DataInputStream dis = new DataInputStream(new FileInputStream("/" )); System.out.println(dis.read()); dis.close(); } }
组合模式 案例分析:
传统方案解决问题分析
将学院看做了学校的子类,将专业看做了学院的子类,相当于从组织大小进行分成
但是这样不能很好的实现管理操作,比如增删改查。
解决方案,把学校,学院,专业当做组织结构,她们之间没有继承关系,而是一个树形结构,可以更好的实现管理操作,=> 组合模式
基本介绍
组合模式(Composite Patterm)),又叫部分整体模式,它创建了对象组的树形结构,将对象组合成树状结构以表示“整体-部分”的层次关系。
组合模式依据树形结构来组合对象,用来表示部分以及整体层次。
组合模式使得用户对单个对象和组合对象的访问具有一致性,即:组合能让客户以一致的方式处理个别对象以及组合对象
代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public abstract class OrganizationComponent { private String name; private String des; protected void add (OrganizationComponent organizationComponent) { throw new UnsupportedOperationException(); } protected void remove (OrganizationComponent organizationComponent) { throw new UnsupportedOperationException(); } public OrganizationComponent (String name, String des) { super (); this .name = name; this .des = des; } protected abstract void print () ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 public class College extends OrganizationComponent { List<OrganizationComponent> organizationComponents = new ArrayList<OrganizationComponent>(); public College (String name, String des) { super (name, des); } @Override protected void add (OrganizationComponent organizationComponent) { organizationComponents.add(organizationComponent); } @Override protected void remove (OrganizationComponent organizationComponent) { organizationComponents.remove(organizationComponent); } @Override public String getName () { return super .getName(); } @Override public String getDes () { return super .getDes(); } @Override protected void print () { System.out.println("--------------" + getName() + "--------------" ); for (OrganizationComponent organizationComponent : organizationComponents) { organizationComponent.print(); } } }public class University extends OrganizationComponent { List<OrganizationComponent> organizationComponents = new ArrayList<OrganizationComponent>(); public University (String name, String des) { super (name, des); } @Override protected void add (OrganizationComponent organizationComponent) { organizationComponents.add(organizationComponent); } @Override protected void remove (OrganizationComponent organizationComponent) { organizationComponents.remove(organizationComponent); } @Override public String getName () { return super .getName(); } @Override public String getDes () { return super .getDes(); } @Override protected void print () { System.out.println("--------------" + getName() + "--------------" ); for (OrganizationComponent organizationComponent : organizationComponents) { organizationComponent.print(); } } }public class Department extends OrganizationComponent { public Department (String name, String des) { super (name, des); } @Override public String getName () { return super .getName(); } @Override public String getDes () { return super .getDes(); } @Override protected void print () { System.out.println(getName()); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class Client { public static void main (String[] args) { OrganizationComponent university = new University("清华大学" , " 中国顶级大学 " ); OrganizationComponent computerCollege = new College("计算机学院" , " 计算机学院 " ); OrganizationComponent infoEngineercollege = new College("信息工程学院" , " 信息工程学院 " ); computerCollege.add(new Department("软件工程" , " 软件工程不错 " )); computerCollege.add(new Department("网络工程" , " 网络工程不错 " )); computerCollege.add(new Department("计算机科学与技术" , " 计算机科学与技术是老牌的专业 " )); infoEngineercollege.add(new Department("通信工程" , " 通信工程不好学 " )); infoEngineercollege.add(new Department("信息工程" , " 信息工程好学 " )); university.add(computerCollege); university.add(infoEngineercollege); infoEngineercollege.print(); } }
注意事项:
注意事项和细节
简化客户端操作,客户端只需要面对一致的对象而不用考虑整体部分或者叶子结点的问题。
具有较强的扩展性,当我们要更改组合对象时,我们只需要调整内部的层次关系,客户端不用做出任何改动。
方便创建出复杂的层次结构。客户端不用理会组合里面的组成细节,容易添加结点或者叶子从而创建出复杂的树形结构
需要便利组织结构,或者处理的对象具有树形结构时,非常适合使用组合模式
要求较高的抽象性, 如果结点和叶子具有很多差异性的话,比如很多方法和属性都不一样,不适合用组合模式
外观模式 基本介绍
外观模式(Facade),也叫“过程模式” : 外观模式为子系统的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用
外观模式通过定义一个一致的接口,用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关系这个子系统的内部细节
原理类图说明(外观模式的角色)
外观类(Facade):为调用端提供统一的调用接口,外观类知道哪些子类系统负责处理请求,从而将调用端的请求代理给适当的子系统对象
调用者(Client):外观接口的调用者
子系统的集合:指模块或者子系统,处理Facade对象指派的任务,他是功能的提供者
外观模式就是解决多个复杂接口带来的使用困难,起到简化用户使用的作用。
源码分析
Mybatis
中的configuration中使用了外观模式,
用户只需要传递一个Object,调用metaObject
方法,而getMetaObeject
方法中则组合了一系列ObjectFactory
,一次判断返回的实例。相当于简化了用户的操作,在内部实现了一系列逻辑、
当我们调用子系统变得很困难的时候,以更高一层的接口来调用子系统,来提高系统使用和复用。
外观模式的注意事项和细节
外观模式对外屏蔽了子系统的细节,因此外观模式降低了客户端对子系统使用的复杂性
外观模式对客户端与子系统的耦合关系,让子系统内部的模块更易维护和扩展
通过合理使用外观模式,可以帮助我们更好的划分访问的层次
当系统需要进行分层设计时,可以考虑使用Facade模式
在维护一个遗留的大型系统时,可能这个系统已经变得非常难以维护和扩展,此时可以考虑为新系统开发一个Facade类,来提供遗留系统的比较清晰简单的接口,让新系统与Facade类交互,提高复用性
不能过多的或者不合理的使用外观模式,使用外观模式好还是直接调用模块好。要以让系统有层次,利于维护为目的。
享元模式 案例分析 小型的外包项目,给客户A做一个产品展示网站,客户A的朋友感觉效果不错,也希望做这样的产品展示网站,但是要求都有些不同:
有客户要求以新闻的形式发布
有客户人要求以博客的形式发布
有客户希望以微信公众号的形式发布
1)需要的网站结构相似度很高,而且都不是高访问量网站,如果分成多个虚拟空间来处理, 相当于一个相同网站 的实例对象很多,造成服务器的资源浪费 2)解决思路:整合到一个网站中,共享其相关的代码和数据,对于硬盘、内存、CPU、数据库空间等服务器资源 都可以达成共享,减少服务器资源 3)对于代码来说,由于是一份实例,维护和扩展都更加容易 4)上面的解决思路就可以使用享元模式来解决
基本介绍
享元模式(Flyweight Pattern)也叫蝇量模式:运用共享技术有效地支持大量细粒度的对象
常用于系统底层开发,解决系统的性能问题。像数据库连接池,里面都是创建好的连接对象,在这些连接对象中有我们需要的则直接拿来用,避免重新创建,如果没有我们需要的,则创建一个
享元模式能够解决重复对象的内存浪费的问题,当系统中有大量相似对象,需要缓冲池时。不需总是创建新对象,可以从缓冲池里拿。这样可以降低系统内存,同时提高效率
享元模式经典的应用场景就是池技术了,String常量池、数据库连接池、缓冲池等等都是享元模式的应用,享元模式是池技术的重要实现方式
对类图的说明 对原理图的说明-即(模式的角色及职责)
FlyWeight
是抽象的享元角色,他是产品的抽象类,同时定义出对象的外部状态和内部状态(后面介绍)的接口或实现
ConcreteFlyWeight
是具体的享元角色,是具体的产品类,实现抽象角色定义相关业务
UnSharedConcreteFlyWeight
是不可共享的角色,一般不会出现在享元工厂。
FlyWeightFactory
享元工厂类,用于构建一个池容器(集合),同时提供从池中获取对象方法
类似线程池,享元共享的数据是不可变的,可以共享资源。如果没有就创建,有就直接拿来用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class User { private String name; public User (String name) { super (); this .name = name; } public String getName () { return name; } public void setName (String name) { this .name = name; } }public abstract class WebSite { public abstract void use (User user) ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public class WebSiteFactory { private HashMap<String, ConcreteWebSite> pool = new HashMap<>(); public WebSite getWebSiteCategory (String type) { if (!pool.containsKey(type)) { pool.put(type, new ConcreteWebSite(type)); } return (WebSite)pool.get(type); } public int getWebSiteCount () { return pool.size(); } }public class ConcreteWebSite extends WebSite { private String type = "" ; public ConcreteWebSite (String type) { this .type = type; } @Override public void use (User user) { System.out.println("网站的发布形式为:" + type + " 在使用中 .. 使用者是" + user.getName()); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class Client { public static void main (String[] args) { WebSiteFactory factory = new WebSiteFactory(); WebSite webSite1 = factory.getWebSiteCategory("新闻" ); webSite1.use(new User("tom" )); WebSite webSite2 = factory.getWebSiteCategory("博客" ); webSite2.use(new User("jack" )); WebSite webSite3 = factory.getWebSiteCategory("博客" ); webSite3.use(new User("smith" )); WebSite webSite4 = factory.getWebSiteCategory("博客" ); webSite4.use(new User("king" )); System.out.println("网站的分类共=" + factory.getWebSiteCount()); } }
源码分析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class FlyWeight { public static void main (String[] args) { Integer x = Integer.valueOf(127 ); Integer y = new Integer(127 ); Integer z = Integer.valueOf(127 ); Integer w = new Integer(127 ); System.out.println(x.equals(y)); System.out.println(x == y ); System.out.println(x == z ); System.out.println(w == x ); System.out.println(w == y ); Integer x1 = Integer.valueOf(200 ); Integer x2 = Integer.valueOf(200 ); System.out.println("x1==x2" + (x1 == x2)); } }
小结:
享元模式中的享可以理解为共享,元理解为对象,就是共享对象模式。比如共享连接池
系统中有大量对象,这些对象消耗大量内存,并且对象的状态大部分可以外部化时,我们就可以考虑选用享元模式
用唯一标识码判断,如果内存中有,则返回这个唯一标识码所标识的对象,用HashMap/HashTable
存储
享元模式大大的减少了对象的创建,降低了程序内存的占用,提高效率。
享元模式提高了系统的复杂度,因为需要分离出内部状态和外部状态,而外部状态具有固化特性,不应该随着内部状态的改变而改变,这点在使用享元模式需要注意。
使用享元模式时,注意划分内部状态和外部状态,并且需要有一个工厂类加以控制,
享元模式经典的应用场景是需要缓冲池,比如String常量池,数据库连接池,以及围棋观赛黑白子对象。
行为型模式 模板模式 案例分析 制作豆浆
基本介绍
模板方法模式(Template Method Pattern) ,在一个抽象类公开了执行它的方法的模板。它的子类可以按需重写方法实现,但调用将以抽象类中定义的方法执行。
简单说模板方法模式定义一个操作中的算法骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构就可以重定义该算法的某些步骤。
原理类图说明
AbstractClass
抽象类,类中实现了模板方法,定义了算法的骨架,具体子类需要去实现 其他的抽象方法
ConcreateClass
实现抽象方法,已完成算法中特点子类的步骤
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public abstract class SoyaMilk { final void make () { select(); if (customerWantCondiments()) { addCondiments(); } soak(); beat(); } void select () { System.out.println("第一步:选择好的新鲜黄豆 " ); } abstract void addCondiments () ; void soak () { System.out.println("第三步, 黄豆和配料开始浸泡, 需要3小时 " ); } void beat () { System.out.println("第四步:黄豆和配料放到豆浆机去打碎 " ); } boolean customerWantCondiments () { return true ; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class RedBeanSoyaMilk extends SoyaMilk { @Override void addCondiments () { System.out.println(" 加入上好的红豆 " ); } }public class PureSoyaMilk extends SoyaMilk { @Override void addCondiments () { } @Override boolean customerWantCondiments () { return false ; } }public class PeanutSoyaMilk extends SoyaMilk { @Override void addCondiments () { System.out.println(" 加入上好的花生 " ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Client { public static void main (String[] args) { System.out.println("----制作红豆豆浆----" ); SoyaMilk redBeanSoyaMilk = new RedBeanSoyaMilk(); redBeanSoyaMilk.make(); System.out.println("----制作花生豆浆----" ); SoyaMilk peanutSoyaMilk = new PeanutSoyaMilk(); peanutSoyaMilk.make(); System.out.println("----制作纯豆浆----" ); SoyaMilk pureSoyaMilk = new PureSoyaMilk(); pureSoyaMilk.make(); } }
在模板方法中添加一个钩子方法默认不做事,子类就可以根据狗子方法来决定是否覆盖这个方法。
这样就可以默认实现了
源码分析
Spring中 IOC
容器初始化的时候 使用了模板模式
小结:
基本思想是,算法只存在于一个地方,也就是在父类中,容易修改。修改算法时,只需要修改父类方法或者已经实现的某些步骤,子类就会继承这些修改。
实现了最大化代码复用。父类的模板方法和已实现的某些步骤会被子类继承而直接使用。
即统一了算法,也提供了很大的灵活性,父类的模板方法确保了算法结构保持不变,同时由子类提供部分步骤的实现。
该模式不足之处是 每一个不同的实现都需要一个子类实现,容易导致类的个数增加导致系统庞大
一般方法都加上关键字final,防止子类重写模板方法
模板方法使用场景:当要完成在某个过程,该过程要执行一系列步骤,这一系列的步骤基本相同,单个别步骤在实现的时候可能不同,通常考虑用模板方法来处理。
命令模式 案例需求 1)我们买了一套智能家电,有照明灯、风扇、冰箱、洗衣机,我们只要在手机上安装 app
就可以控制对这些家电 工作。 2)这些智能家电来自不同的厂家,我们不想针对每一种家电都安装一个App
,分别控制,我们希望只要一个app
就可以控制全部智能家电。 3)要实现一个app
控制所有智能家电的需要,则每个智能家电厂家都要提供一个统一的接口给app调用,这时就 可以考虑使用命令模式。 4)命令模式可将“动作的请求者”从“动作的执行者”对象中解耦出来. 5)在我们的例子中,动作的请求者是手机 app
,动作的执行者是每个厂商的一个家电产品
基本介绍
命令模式(Command Pattern):在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收 者是谁,也不知道被请求的操作是哪个, 我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计
命名模式使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活,实现解耦。
在命名模式中,会将一个请求封装为一个对象,以便使用不同参数来表示不同的请求(即命名),同时命令模式 也支持可撤销的操作。
通俗易懂的理解:将军发布命令,士兵去执行。其中有几个角色:将军(命令发布者)、士兵(命令的具体执 行者)、命令(连接将军和士兵)。lInvoker
是调用者(将军),Receiver是被调用者(士兵),MyCommand
是命令,实现了Command接口,持有接收对象
原理类图分析 代码实现 类图分析:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 public interface Command { public void execute () ; public void undo () ; }public class LightOffCommand implements Command { LightReceiver light; public LightOffCommand (LightReceiver light) { this .light = light; } @Override public void execute () { light.off(); } @Override public void undo () { light.on(); } }public class LightOnCommand implements Command { LightReceiver light; public LightOnCommand (LightReceiver light) { this .light = light; } @Override public void execute () { light.on(); } @Override public void undo () { light.off(); } }public class NoCommand implements Command { @Override public void execute () { } @Override public void undo () { } }public class LightReceiver { public void on () { System.out.println(" 电灯打开了.. " ); } public void off () { System.out.println(" 电灯关闭了.. " ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 public class RemoteController { Command[] onCommands; Command[] offCommands; Command undoCommand; public RemoteController () { onCommands = new Command[5 ]; offCommands = new Command[5 ]; for (int i = 0 ; i < 5 ; i++) { onCommands[i] = new NoCommand(); offCommands[i] = new NoCommand(); } } public void setCommand (int no, Command onCommand, Command offCommand) { onCommands[no] = onCommand; offCommands[no] = offCommand; } public void onButtonWasPushed (int no) { onCommands[no].execute(); undoCommand = onCommands[no]; } public void offButtonWasPushed (int no) { offCommands[no].execute(); undoCommand = offCommands[no]; } public void undoButtonWasPushed () { undoCommand.undo(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 public class Client { public static void main (String[] args) { LightReceiver lightReceiver = new LightReceiver(); LightOnCommand lightOnCommand = new LightOnCommand(lightReceiver); LightOffCommand lightOffCommand = new LightOffCommand(lightReceiver); RemoteController remoteController = new RemoteController(); remoteController.setCommand(0 , lightOnCommand, lightOffCommand); System.out.println("--------按下灯的开按钮-----------" ); remoteController.onButtonWasPushed(0 ); System.out.println("--------按下灯的关按钮-----------" ); remoteController.offButtonWasPushed(0 ); System.out.println("--------按下撤销按钮-----------" ); remoteController.undoButtonWasPushed(); } }
源码分析
Spring中的JDBCTemplate中使用了模板模式
小结
注意事项和细节
将发起请求的对象与执行请求的对象解耦。发起请求的对象是调用者,调用者只要调用命令对象的execute()方法就可以让接收着工作,而不必知道记得接收着对象谁,如何实现的,命令对象会负责让接受者执行请求的动作,也就是“请求发起者”和“请求执行者”之间的解耦是通过命令对象实现的,命令对象起到了扭到桥梁的作用。
容易设计一个命令队列,只要把命令对象放到队列,就可以实现多线程的命令
容易实现对请求的撤销和重做
命令模式的不足:可能导致某些系统有过多的具体命令类,增加了系统的复杂度,这点在使用的时候要注意
空命令也是一种设计模式,它为我们省去了判空的操作,在上面的案例,如果没有用空命令,那么每一个按键我们都要做判空操作,对编码带来了一定的麻烦。
命令模式经典的应用场景:界面的一个按钮都是一条命令,模拟CMD(DOS)命令,订单的撤销/恢复、触发反馈机制