设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结,使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、高内聚低耦合。虽然GoF设计模式只有23个,但是它们各具特色,每个模式都为某一个可重复的设计问题提供了一套解决方案。根据它们的用途,设计模式可分为创建型,结构型和行为型三种,其中创建型模式主要用于描述如何创建对象,结构型模式主要用于描述如何实现类或对象的组合,行为型模式主要用于描述类或对象怎样交互以及怎样分配职责。在GoF23种设计模式中包含5种创建型设计模式、7种结构型设计模式和11种行为型设计模式。此外,根据某个模式主要是用于处理类之间的关系还是对象之间的关系,设计模式还可以分为类模式和对象模式。
设计模式练习网站: https://java-design-patterns.com/patterns
设计模式类型 | 设计模式名称 | 介绍 | 学习难度 | 使用频率 |
---|---|---|---|---|
创建型模式(6种) | 单例模式 | 保证一个类仅有一个对象,并提供一个访问它的全局访问点。 | ★☆☆☆☆ | ★★★★☆ |
简单工厂模式 | 定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。 | ★★☆☆☆ | ★★★★★ | |
工厂方法模式 | 定义一个用于创建对象的接口,让子类决定将哪一个类实例化。 | ★★☆☆☆ | ★★★★★ | |
抽象工厂模式 | 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们的具体类。 | ★★★★☆ | ★★★★★ | |
原型模式 | 使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。 | ★★★☆☆ | ★★★☆☆ | |
建造者模式 | 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。 | ★★★★☆ | ★★☆☆☆ | |
结构型模式(7种) | 适配器模式 | 将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 | ★★☆☆☆ | ★★★★☆ |
桥接模式 | 将抽象部分与它的实现部分分离,使他们都可以独立地变化。 | ★★★☆☆ | ★★★☆☆ | |
组合模式 | 组合多个对象形成树形结构以表示具有“整体—部分”关系的层次结构。组合模式对单个对象和组合对象的使用具有一致性。 | ★★★☆☆ | ★★★★☆ | |
装饰模式 | 动态地给一个对象增加一些额外的职责,就增加对象功能来说,装饰模式比生成子类实现更为灵活。 | ★★★☆☆ | ★★★☆☆ | |
外观模式 | 为子系统中的一组接口提供一个统一的入口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。 | ★☆☆☆☆ | ★★★★★ | |
享元模式 | 运用共享技术有效地支持大量细粒度的对象。 | ★★★★☆ | ★☆☆☆☆ | |
代理模式 | 为其他对象提供一个代理以控制对这个对象的访问。 | ★★★☆☆ | ★★★★☆ | |
行为模式(11种) | 职责链模式 | 为解除请求的发送者和接收者之间的耦合,而使多个对象都有机会处理这个请求。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它。 | ★★★☆☆ | ★★☆☆☆ |
命令模式 | 将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可取消的操作。 | ★★★☆☆ | ★★★★☆ | |
解释器模式 | 定义一个语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。 | ★★★★★ | ★☆☆☆☆ | |
迭代器模式 | 提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露该对象的内部表示。 | ★★★☆☆ | ★★★★★ | |
中介者模式 | 用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显示地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。 | ★★★☆☆ | ★★☆☆☆ | |
备忘录模式 | 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保持该状态,这样以后就可以将该对象恢复到保存的状态。 | ★★☆☆☆ | ★★☆☆☆ | |
观察者模式 | 定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新。 | ★★★☆☆ | ★★★★★ | |
状态模式 | 允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它所属的类。 | ★★★☆☆ | ★★★☆☆ | |
策略模式 | 定义一系列的算法,把它们一个个封装起来,并且使他们可相互替换。本模式使得算法的变化可以独立于使用它的客户。 | ★☆☆☆☆ | ★★★★☆ | |
模板方法模式 | 定义一个操作中的算法的骨架,而将一些步骤延迟到子类。 | ★★☆☆☆ | ★★★☆☆ | |
访问者模式 | 表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素类别的前提下定义作用于这些元素的新操作。 | ★★★★☆ | ★☆☆☆☆ |
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。单例模式是一种对象创建型模式。单例模式设计就是采用一定的方法保证在整个程序中,对某个类只能存在一个对象的实例,并且该类只提供一个取得其对象实例的方法。
单例模式作用:
单例模式主要应用在需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多、重量级对象,但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session
工厂等)。单例模式在Java中有6种实现。
名称 | 优点 | 缺点 |
---|---|---|
饿汉式 | 线程安全,写法简单 | 不懒加载,可能造成浪费 |
懒汉式(线程不安全) | 懒加载 | 线程不安全 |
懒汉式(线程安全) | 线程安全,懒加载 | 效率很低,反序列化破坏单例 |
双重校验锁 | 线程安全,懒加载 | 反序列化破坏单例 |
静态内部类式 | 线程安全,懒加载 | 反序列化破坏单例 |
枚举式 | 防止反射攻击,反序列化创建对象,写法简单 | 不能传参,不能继承其他类 |
class Singleton { private Singleton() {} private static final Singleton instance = new Singleton(); public static Singleton getInstance() { return instance; } }
这种写法比较简单,就是在类加载的时候就完成实例化。避免了线程同步问题。但是在类装载的时候就完成实例化,没有达到懒加载的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。
class Singleton { private Singleton() { } private static Singleton instance; public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
起到了懒加载的效果,但是只能在单线程下使用。如果在多线程下,一个线程进入了if (singleton == null)
判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例,所以在多线程环境下不可使用这种方式。
class Singleton { private static Singleton instance; private Singleton() { } public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
虽然解决了线程安全问题但是效率太低了,每个线程在想获得类的实例时候,执行getInstance()
方法都要进行同步。而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接 return
就行了。
class Singleton { /** * volatile在这作用: 禁止JVM指令重排 */ private static volatile Singleton instance; private Singleton() { } public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
Double-Check
概念是多线程开发中常使用到的,如代码中所示,我们进行了两次if (singleton == null)
检查,这样就可以保证线程安全了。这样,实例化代码只用执行一次,后面再次访问时,判断if (singleton == null)
,直接return
实例化对象,也避免的反复进行方法同步。
class Singleton { private Singleton() {} private static class InnerClass { private static final Singleton instance = new Singleton(); } public static Singleton getInstance() { return InnerClass.instance; } }
这种方式同样利用了classloder
的机制来保证初始化instance
时只有一个线程,它跟饿汉式不同的是(很细微的差别):饿汉式是只要Singleton
类被装载了,那么instance
就会被实例化(没有达到lazy loading
效果),而这种方式是Singleton
类被装载了,instance
不一定被初始化。因为SingletonHolder
类没有被主动使用,只有显示通过调用getInstance
方法时,才会显示装载SingletonHolder
类,从而实例化instance
。想象一下,如果实例化instance
很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton
类加载时就实例化,因为我不能确保Singleton
类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance
显然是不合适的。这个时候,这种方式相比饿汉式更加合理。
enum Singleton { INSTAMCE; Singleton() { } }
这种方式是《Effective Java》作者Josh Bloch
提倡的方式,借助 JDK1.5 中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。
上述单例模式6种写法除了枚举式单例外其他5种写法都存在序列化问题。序列化可以破坏单例,原因是序列化会通过反射调用无参数的构造方法创建一个新的对象。
要想防止序列化对单例的破坏,只要在单例类中定义readResolve
方法就可以解决该问题。原理是反序列化时,会通过反射的方式调用要被反序列化的类的readResolve
方法,所以在这个方法中可以自定义序列化的方式。主要在Singleton
类中定义readResolve
方法,并在该方法中指定要返回的对象的生成策略,就可以防止单例被破坏。以双重校验锁为例,在该单例类中插入readResolve
方法。
public class Singleton implements Serializable{ private volatile static Singleton singleton; private Singleton (){} public static Singleton getSingleton() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } private Object readResolve() { return singleton; } }
工厂模式是将实例化的对象代码提取出来,放到一个类中统一管理和维护,达到和主项目的依赖关系的解耦,从而提高项目的扩展和维护性。创建对象实例时不要直接new
类,而是把这个new
类的动作放在一个工厂的方法中并返回。不要让类继承具体的类,而是继承抽象类或者实现接口。
简单工厂模式,定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。因为在简单工厂模式中用于创建实例的方法是静态方法,因此简单工厂模式又被称为静态工厂方法模式,它属于类创建型模式。
public class SimpleFactoryDemo { public static void main(String[] args) { SimpleFactory.getTest(1); } } class SimpleFactoryImpl1 implements SimpleFactoryInterface { @Override public void test() { System.out.println("i am simpleFactory 1 ..."); } } class SimpleFactoryImpl2 implements SimpleFactoryInterface { @Override public void test() { System.out.println("i am simpleFactory 2 ..."); } } class SimpleFactory { public static void getTest(int n) { switch (n) { case 1: SimpleFactoryImpl1 simpleFactory = new SimpleFactoryImpl1(); simpleFactory.test(); break; case 2: SimpleFactoryImpl2 simpleFactoryImpl2 = new SimpleFactoryImpl2(); simpleFactoryImpl2.test(); break; default: } } }
简单工厂模式的优点是实现了对象创建和使用的分离,符合面向接口编程的思想,从而实现代码解耦。然而,如果工厂内包含的逻辑和职责过多,会导致代码臃肿和扩展困难。尤其是在产品类型较多时,添加新产品需要修改工厂逻辑,可能导致系统维护和扩展变得复杂。简单工厂模式适用于创建对象较少且业务逻辑不复杂的场景。
工厂方法模式,定义一个用于创建对象的接口,让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。工厂方法模式又简称为工厂模式,又可称作虚拟构造器模式或多态工厂模式。工厂方法模式是一种类创建型模式。
public class FactoryMethodDemo { public static void main(String[] args) { FoodFactory f = new ColdRiceNoodleFactory(); f.getFood().eat(); // 扩展需要增加产品及相应产品工厂并实现相关接口 } } interface Food { void eat(); } interface FoodFactory { Food getFood(); } class RiceNoodle implements Food{ @Override public void eat() { System.out.println("eat rice noodle ..."); } } class RiceNoodleFactory implements FoodFactory{ @Override public Food getFood() { return new RiceNoodle(); } } class ColdRiceNoodle implements Food { @Override public void eat() { System.out.println("eat cold rice noodle ..."); } } class ColdRiceNoodleFactory implements FoodFactory { @Override public Food getFood() { return new ColdRiceNoodle(); } }
工厂方法模式是简单工厂模式的延伸,它继承了简单工厂模式的优点,同时还弥补了简单工厂模式的不足。使用工厂方法模式扩展时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品,只要添加一个具体工厂和具体产品就可以了,这样系统的可扩展性也就变得非常好,完全符合“开闭原则”。但是在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。
抽象工厂模式,提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,它是一种对象创建型模式。
interface Food { void eat(); } interface FoodFactory { ColdRiceNoodle getColdRiceNoodle(); RiceNoodle getRiceNoodle(); } class RiceNoodle implements Food{ @Override public void eat() { System.out.println("eating rice noodle"); } } class ColdRiceNoodle implements Food{ @Override public void eat() { System.out.println("eating cold rice noodle"); } } class RiceNoodleFactory implements FoodFactory { @Override public ColdRiceNoodle getColdRiceNoodle() { return new ColdRiceNoodle(); } @Override public RiceNoodle getRiceNoodle() { return new RiceNoodle(); } } class ColdRiceNoodleFactory implements FoodFactory { @Override public ColdRiceNoodle getColdRiceNoodle() { return new ColdRiceNoodle(); } @Override public RiceNoodle getRiceNoodle() { return new RiceNoodle(); } }
抽象工厂模式是工厂方法模式的进一步延伸,仍然具有工厂方法和简单工厂的优点。抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易,所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。但是抽象工厂也存在一些缺点,增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大的不便,违背了“开闭原则”。
在Java中通过new
关键字创建的对象是非常繁琐的,在我们需要大量对象的情况下,原型模式就是我们可以考虑实现的方式。使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式是一种对象创建型模式。
原型模式我们也称为克隆模式,即一个某个对象为原型克隆出来一个一模一样的对象,该对象的属性和原型对象一模一样,而且对于原型对象没有任何影响。原型模式的克隆方式有两种,浅克隆和深度克隆;浅克隆和深克隆的主要区别在于是否支持引用类型的成员变量的复制。
在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。
public class ShallowClone { public static void main(String[] args) { CloneHuman cloneHuman = new CloneHuman("黑色","大眼睛","高鼻梁","大嘴巴",new Date(123231231231L)); for (int i = 0; i < 20; i++) { try { CloneHuman clone = (CloneHuman)cloneHuman.clone(); System.out.printf("头发:%s,眼睛:%s,鼻子:%s,嘴巴:%s,生日:%s",clone.getHair(),clone.getEye(),clone.getNodes(),clone.getMouse(),clone.getBirth()); System.out.println(); System.out.println("浅克隆,引用类型地址比较:" + (cloneHuman.getBirth() == clone.getBirth())); } catch (CloneNotSupportedException e) { System.out.println(e.getMessage()); } } } } class CloneHuman implements Cloneable { private String hair; private String eye; private String nodes; private String mouse; private Date birth; public String getHair() { return hair; } public void setHair(String hair) { this.hair = hair; } public String getEye() { return eye; } public void setEye(String eye) { this.eye = eye; } public String getNodes() { return nodes; } public void setNodes(String nodes) { this.nodes = nodes; } public String getMouse() { return mouse; } public void setMouse(String mouse) { this.mouse = mouse; } public CloneHuman(String hair, String eye, String nodes, String mouse,Date brith) { this.hair = hair; this.eye = eye; this.nodes = nodes; this.mouse = mouse; this.birth = brith; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } public Date getBirth() { return birth; } public void setBirth(Date birth) { this.birth = birth; } }
Java提供的Cloneable
接口和Serializable
接口的代码非常简单,它们都是空接口,这种空接口也称为标识接口,标识接口中没有任何方法的定义,其作用是告诉JRE这些接口的实现类是否具有某个功能,如是否支持克隆、是否支持序列化等。
应该注意的是,clone
方法并不是Cloneable
接口的方法,而是Object
的一个protected
方法。Cloneable
接口只是规定,如果一个类没有实现Cloneable
接口又调用了clone()
方法,就会抛出 CloneNotSupportedException
。
在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制。深克隆有两种实现方式,第一种是在浅克隆的基础上实现,第二种是通过序列化和反序列化实现。
在浅克隆的基础上实现:
public class DeepClone { public static void main(String[] args) { CloneHuman cloneHuman = new CloneHuman("黑色","大眼睛","高鼻梁","大嘴巴",new Date(123231231231L)); for (int i = 0; i < 20; i++) { try { CloneHuman clone = (CloneHuman)cloneHuman.clone(); System.out.printf("头发:%s,眼睛:%s,鼻子:%s,嘴巴:%s,生日:%s",clone.getHair(),clone.getEye(),clone.getNodes(),clone.getMouse(),clone.getBirth()); System.out.println(); System.out.println("深克隆,引用类型地址比较:" + (cloneHuman.getBirth() == clone.getBirth())); } catch (CloneNotSupportedException e) { System.out.println(e.getMessage()); } } } } class CloneHuman implements Cloneable { private String hair; private String eye; private String nodes; private String mouse; private Date birth; public String getHair() { return hair; } public void setHair(String hair) { this.hair = hair; } public String getEye() { return eye; } public void setEye(String eye) { this.eye = eye; } public String getNodes() { return nodes; } public void setNodes(String nodes) { this.nodes = nodes; } public String getMouse() { return mouse; } public void setMouse(String mouse) { this.mouse = mouse; } public CloneHuman(String hair, String eye, String nodes, String mouse,Date brith) { this.hair = hair; this.eye = eye; this.nodes = nodes; this.mouse = mouse; this.birth = brith; } @Override protected Object clone() throws CloneNotSupportedException { CloneHuman human = (CloneHuman)super.clone(); human.birth = (Date)this.birth.clone(); return human; } public Date getBirth() { return birth; } public void setBirth(Date birth) { this.birth = birth; } }
序列化反序列化实现深克隆:
public class DeepClone2 { public static void main(String[] args) throws IOException, ClassNotFoundException { CloneHuman2 cloneHuman1 = new CloneHuman2("黑色","大眼睛","高鼻梁","大嘴巴",new Date(123231231231L)); // 使用序列化和反序列化实现深克隆 ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(cloneHuman1); byte[] bytes = bos.toByteArray(); ByteArrayInputStream bis = new ByteArrayInputStream(bytes); ObjectInputStream ois = new ObjectInputStream(bis); // 克隆好的对象 CloneHuman2 cloneHuman2 = (CloneHuman2) ois.readObject(); System.out.println("深克隆,引用类型地址比较:" + (cloneHuman1.getBirth() == cloneHuman2.getBirth())); } } class CloneHuman2 implements Cloneable, Serializable { private String hair; private String eye; private String nodes; private String mouse; private Date birth; public String getHair() { return hair; } public void setHair(String hair) { this.hair = hair; } public String getEye() { return eye; } public void setEye(String eye) { this.eye = eye; } public String getNodes() { return nodes; } public void setNodes(String nodes) { this.nodes = nodes; } public String getMouse() { return mouse; } public void setMouse(String mouse) { this.mouse = mouse; } public CloneHuman2(String hair, String eye, String nodes, String mouse,Date brith) { this.hair = hair; this.eye = eye; this.nodes = nodes; this.mouse = mouse; this.birth = brith; } @Override protected Object clone() throws CloneNotSupportedException { CloneHuman2 human = (CloneHuman2)super.clone(); human.birth = (Date)this.birth.clone(); return human; } public Date getBirth() { return birth; } public void setBirth(Date birth) { this.birth = birth; } }
原型模式作为一种快速创建大量相同或相似对象的方式,在软件开发中应用较为广泛,很多软件提供的复制和粘贴操作就是原型模式的典型应用。通过clone
的方式在获取大量对象的时候性能开销基本没有什么影响,而new
的方式随着实例的对象越来越多,性能会急剧下降,所以原型模式是一种比较重要的获取实例的方式。
优点:
缺点:
使用场景:
clone
的方法创建一个对象,然后由工厂方法提供给调用者。Spring
中bean
的创建实际就是两种:单例模式和原型模式。建造者模式,将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式是一种对象创建型模式。建造者模式是较为复杂的创建型模式,它将客户端与包含多个组成部分的复杂对象的创建过程分离,客户端无须知道复杂对象的内部组成部分与装配方式,只需要知道所需建造者的类型即可。它关注如何一步一步创建一个的复杂对象,不同的具体建造者定义了不同的创建过程,且具体建造者相互独立,增加新的建造者非常方便,无须修改已有代码,系统具有较好的扩展性。
public class MainTest { public static void main(String[] args) { Director director = new Director(); Builder commonBuilder = new CommonRole(); director.construct(commonBuilder); Role commonRole = commonBuilder.getRole(); System.out.println(commonRole); } } class Role { private String head; private String body; private String foot; private String sp; private String hp; private String name; public void setSp(String sp) { this.sp = sp; } public String getSp() { return sp; } public void setHp(String hp) { this.hp = hp; } public String getHp() { return hp; } public void setName(String name) { this.name = name; } public String getName() { return name; } @Override public String toString() { return "Role{" + "head='" + head + '\'' + ", body='" + body + '\'' + ", foot='" + foot + '\'' + ", sp='" + sp + '\'' + ", hp='" + hp + '\'' + ", name='" + name + '\'' + '}'; } } abstract class Builder { public abstract void builderHead(); public abstract void builderBody(); public abstract void builderFoot(); public abstract void builderSp(); public abstract void builderHp(); public abstract void builderName(); public Role getRole() { return new Role(); } } class CommonRole extends Builder { private Role role = new Role(); @Override public void builderHead() { System.out.println("building head ....."); } @Override public void builderBody() { System.out.println("building body ....."); } @Override public void builderFoot() { System.out.println("building foot ....."); } @Override public void builderSp() { role.setSp("100"); } @Override public void builderHp() { role.setHp("100"); } @Override public void builderName() { role.setName("lucy"); } @Override public Role getRole() { return role; } } class Director { public void construct(Builder builder) { builder.builderHead(); builder.builderBody(); builder.builderFoot(); builder.builderHp(); builder.builderSp(); builder.builderName(); } }
优点:
缺点:
使用场景:
适配器模式,将一个接口转换成客户希望的另一个接口(指广义的接口,它可以表示一个方法或者方法的集合),使接口不兼容的那些类可以一起工作,其别名为包装器。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。
在适配器模式中,我们通过增加一个新的适配器类来解决接口不兼容的问题,使得原本没有任何关系的类可以协同工作。根据适配器类与适配者类的关系不同,适配器模式可分为对象适配器和类适配器两种,在对象适配器模式中,适配器与适配者之间是关联关系。在类适配器模式中,适配器与适配者之间是继承或实现关系。
由于在Java中不支持多重继承,而且有破坏封装之嫌。所以提倡多用组合少用继承,在实际开发中推荐使用对象适配器模式。
public class MainTest { public static void main(String[] args) { new RedHat(new Linux()).useInputMethod(); System.out.println("=============="); new Win10(new Windows()).useInputMethod(); System.out.println("=============="); Adapter adapter = new Adapter(new Windows()); new RedHat(adapter).useInputMethod(); } } interface LinuxSoftware { void inputMethod(); } interface WindowsSoftware { void inputMethod(); } class Linux implements LinuxSoftware { @Override public void inputMethod() { System.out.println("linux 系统输入法 ..."); } } class Windows implements WindowsSoftware { @Override public void inputMethod() { System.out.println("windows 系统输入法 ..."); } } class RedHat { private LinuxSoftware linuxSoftware; public RedHat(LinuxSoftware linuxSoftware) { this.linuxSoftware = linuxSoftware; } public void useInputMethod() { System.out.println("开始使用 redHat 系统输入法 ..."); linuxSoftware.inputMethod(); System.out.println("结束使用 redHat 系统输入法 ..."); } } class Win10 { private WindowsSoftware windowsSoftware; public Win10(WindowsSoftware windowsSoftware) { this.windowsSoftware = windowsSoftware; } public void useInputMethod() { System.out.println("开始使用 win10 系统输入法 ..."); windowsSoftware.inputMethod(); System.out.println("结束使用 win10 系统输入法 ..."); } } // 在 Linux 系统上使用 windows 输入法 class Adapter implements LinuxSoftware{ private WindowsSoftware windowsSoftware; public Adapter(WindowsSoftware windowsSoftware) { this.windowsSoftware = windowsSoftware; } @Override public void inputMethod() { windowsSoftware.inputMethod(); } }
类适配器模式和对象适配器模式最大的区别在于适配器和适配者之间的关系不同,对象适配器模式中适配器和适配者之间是关联关系,而类适配器模式中适配器和适配者是继承关系。
public class MainTest { public static void main(String[] args) { new RedHat(new Linux()).useInputMethod(); System.out.println("=============="); new Win10(new Windows()).useInputMethod(); System.out.println("=============="); Adapter adapter = new Adapter(); new RedHat(adapter).useInputMethod(); } } interface LinuxSoftware { void inputMethod(); } interface WindowsSoftware { void inputMethod(); } class Linux implements LinuxSoftware { @Override public void inputMethod() { System.out.println("linux 系统输入法 ..."); } } class Windows implements WindowsSoftware { @Override public void inputMethod() { System.out.println("windows 系统输入法 ..."); } } class RedHat { private LinuxSoftware linuxSoftware; public RedHat(LinuxSoftware linuxSoftware) { this.linuxSoftware = linuxSoftware; } public void useInputMethod() { System.out.println("开始使用 redHat 系统输入法 ..."); linuxSoftware.inputMethod(); System.out.println("结束使用 redHat 系统输入法 ..."); } } class Win10 { private WindowsSoftware windowsSoftware; public Win10(WindowsSoftware windowsSoftware) { this.windowsSoftware = windowsSoftware; } public void useInputMethod() { System.out.println("开始使用 win10 系统输入法 ..."); windowsSoftware.inputMethod(); System.out.println("结束使用 win10 系统输入法 ..."); } } // 在 Linux 系统上使用 windows 输入法 class Adapter extends Windows implements LinuxSoftware{ @Override public void inputMethod() { super.inputMethod(); } }
适配器模式将现有接口转化为客户类所期望的接口,实现了对现有类的复用,它是一种使用频率非常高的设计模式,在软件开发中得以广泛应用,在Spring
等开源框架、驱动程序设计(如JDBC
中的数据库驱动程序)中也使用了适配器模式。
优点:
缺点:
使用场景
桥接模式,将实现与抽象放在两个不同的类层次中,使两个层次可以独立改变。它是一种对象结构型模式,又称为柄体模式或接口模式。桥接模式是一种很实用的结构型设计模式,如果软件系统中某个类存在两个独立变化的维度,通过该模式可以将这两个维度分离出来,使两者可以独立扩展,让系统更加符合“单一职责原则”。
如手机制造,内存是一个公司生产,芯片是另一个公司生产,而品牌又是另一个公司。我们需要什么样子的手机就把相应的芯片、内存组装起来。桥接模式就是把两个不同维度的东西桥接起来。
public class MainTest { public static void main(String[] args) { Phone_A phone_a = new Phone_A(); phone_a.setAbstractChip(new Chip_A()); phone_a.setAbstractMemory(new Memory_A()); phone_a.finished(); System.out.println("================="); Phone_B phone_b = new Phone_B(); phone_b.setAbstractChip(new Chip_B()); phone_b.setAbstractMemory(new Memory_A()); phone_b.finished(); } } abstract class AbstractMemory { protected abstract void size(); } abstract class AbstractChip { protected abstract void type(); } abstract class AbstractPhone { protected AbstractMemory abstractMemory; protected AbstractChip abstractChip; public void setAbstractChip(AbstractChip abstractChip) { this.abstractChip = abstractChip; } public void setAbstractMemory(AbstractMemory abstractMemory) { this.abstractMemory = abstractMemory; } protected abstract void finished(); } class Memory_A extends AbstractMemory{ @Override protected void size() { System.out.println("create 6G of memory ..."); } } class Memory_B extends AbstractMemory{ @Override protected void size() { System.out.println("create 8G of memory ..."); } } class Chip_A extends AbstractChip { @Override protected void type() { System.out.println("snapdragon 888 chip ..."); } } class Chip_B extends AbstractChip { @Override protected void type() { System.out.println("A14 chip ..."); } } class Phone_A extends AbstractPhone{ @Override protected void finished() { abstractMemory.size(); abstractChip.type(); System.out.println("phone_a assembly completed ..."); } } class Phone_B extends AbstractPhone{ @Override protected void finished() { abstractMemory.size(); abstractChip.type(); System.out.println("phone_b assembly completed ..."); } }
桥接模式是设计Java虚拟机和实现JDBC等驱动程序的核心模式之一,应用较为广泛。在软件开发中如果一个类或一个系统有多个变化维度时,都可以尝试使用桥接模式对其进行设计。桥接模式为多维度变化的系统提供了一套完整的解决方案,并且降低了系统的复杂度。
优点:
缺点:
使用场景:
组合模式,组合多个对象形成树形结构以表示具有“整体—部分”关系的层次结构。组合模式对单个对象和组合对象的使用具有一致性,组合模式又可以称为“整体—部分”模式,它是一种对象结构型模式。组合模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。
public class MainTest { public static void main(String[] args) { Tree tree = new Tree(); tree.add(new LeftNode()); tree.add(new RightNode()); tree.implmethod(); } } abstract class AbstractTree { protected abstract void add(AbstractTree node); protected abstract void remove(AbstractTree node); protected abstract void implmethod(); } class LeftNode extends AbstractTree { @Override protected void add(AbstractTree node) { System.out.println("Exception: the method is not supported. "); } @Override protected void remove(AbstractTree node) { System.out.println("Exception: the method is not supported. "); } @Override protected void implmethod() { System.out.println("left node method ..."); } } class RightNode extends AbstractTree { @Override protected void add(AbstractTree node) { System.out.println("Exception: the method is not supported. "); } @Override protected void remove(AbstractTree node) { System.out.println("Exception: the method is not supported. "); } @Override protected void implmethod() { System.out.println("right node method ..."); } } class Tree extends AbstractTree { private ArrayList treeList = new ArrayList<>(); @Override protected void add(AbstractTree node) { treeList.add(node); } @Override protected void remove(AbstractTree node) { treeList.remove(node); } @Override protected void implmethod() { System.out.println("tree node"); for (AbstractTree node : treeList) { node.implmethod(); } } }
组合模式使用面向对象的思想来实现树形结构的构建与处理,描述了如何将容器对象和叶子对象进行递归组合,实现简单,灵活性好。由于在软件开发中存在大量的树形结构,因此组合模式是一种使用频率较高的结构型设计模式。在XML解析、组织结构树处理、文件系统设计等领域,组合模式都得到了广泛应用。
优点:
缺点:
使用场景:
装饰模式,动态地给一个对象增加一些额外的职责,就增加对象功能来说,装饰模式比生成子类实现更为灵活。装饰模式是一种对象结构型模式。
public class MainTest { public static void main(String[] args) { Drink coffee = new ShortBlock(); System.out.println("订单价格:"+ coffee.cost()); System.out.println("订单描述:" + coffee.getDesc()); System.out.println("====================="); coffee = new Chocolate(coffee); System.out.println("订单价格:"+ coffee.cost()); System.out.println("订单描述:" + coffee.getDesc()); System.out.println("====================="); coffee = new Milk(coffee); System.out.println("订单价格:"+ coffee.cost()); System.out.println("订单描述:" + coffee.getDesc()); } } abstract class Drink { public String desc; private double price = 0.0; public void setDesc(String desc) { this.desc = desc; } public String getDesc() { return desc; } public void setPrice(double price) { this.price = price; } public double getPrice() { return price; } protected abstract double cost(); } class ShortBlock extends Drink{ public ShortBlock() { super.setPrice(3.0); super.setDesc("ShortBlock"); } @Override protected double cost() { return super.getPrice(); } } class LongBlock extends Drink { public LongBlock() { super.setPrice(5.0); super.setDesc("LongBlock"); } @Override protected double cost() { return super.getPrice(); } } class CoffeeShop extends Drink { private final Drink coffee; protected CoffeeShop(Drink coffee) { this.coffee = coffee; } @Override protected double cost() { return super.getPrice() + coffee.cost(); } @Override public String getDesc() { return desc + " " + getPrice() +" && "+ coffee.getDesc(); } } class Milk extends CoffeeShop { public Milk(Drink seasoning) { super(seasoning); super.setDesc("Milk"); super.setPrice(6); } } class Chocolate extends CoffeeShop { public Chocolate(Drink seasoning) { super(seasoning); super.setDesc("Chocolate"); super.setPrice(4); } }
装饰模式降低了系统的耦合度,可以动态增加或删除对象的职责,并使得需要装饰的具体构件类和具体装饰类可以独立变化,以便增加新的具体构件类和具体装饰类。在软件开发中,装饰模式应用较为广泛,例如在JavaIO
中的输入流和输出流的设计、javax.swing
包中一些图形界面构件功能的增强等地方都运用了装饰模式。
一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀,装饰者模式主要解决这个问题。
优点:
缺点:
使用场景:
外观模式,为子系统中的一组接口提供一个统一的入口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式又称为门面模式,它是一种对象结构型模式。外观模式是迪米特法则的一种具体实现,通过引入一个新的外观角色可以降低原有系统的复杂度,同时降低客户类与子系统的耦合度。
public class MainTest { public static void main(String[] args) { Facade facade = new Facade(); facade.aTest(); } } class Facade { private final A a; private final B b; private final C c; private final D d; public Facade() { this.a = A.getInstance(); this.b = B.getInstance(); this.c = C.getInstance(); this.d = D.getInstance(); } public void aTest() { a.aTest(); b.aTest(); c.aTest(); d.aTest(); } } /** * A调用B类,A调用C类,B调用C类,D调用B类,D调用C类 */ class A { private final static A instance = new A(); private A(){} public static A getInstance() { return instance; } public void aTest() { B.getInstance().aTest(); C.getInstance().aTest(); System.out.println("A class test method ..."); } } class B { private final static B instance = new B(); private B(){} public static B getInstance() { return instance; } public void aTest() { C.getInstance().aTest(); System.out.println("B class test method ..."); } } class C { private final static C instance = new C(); private C(){} public static C getInstance() { return instance; } public void aTest() { System.out.println("C class test method ..."); } } class D { private final static D instance = new D(); private D(){} public static D getInstance() { return instance; } public void aTest() { B.getInstance().aTest(); C.getInstance().aTest(); System.out.println("D class test method ..."); } }
外观模式的主要目的在于降低系统的复杂程度,在面向对象软件系统中,类与类之间的关系越多,不能表示系统设计得越好,反而表示系统中类之间的耦合度太大,这样的系统在维护和修改时都缺乏灵活性,因为一个类的改动会导致多个类发生变化,而外观模式的引入在很大程度上降低了类与类之间的耦合关系。引入外观模式之后,增加新的子系统或者移除子系统都非常方便,客户类无须进行修改(或者极少的修改),只需要在外观类中增加或移除对子系统的引用即可。从这一点来说,外观模式在一定程度上并不符合开放封闭原则,增加新的子系统需要对原有系统进行一定的修改,虽然这个修改工作量不大。
优点:
缺点:
使用场景:
享元模式,运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,它是一种对象结构型模式。
public class MainTest { public static void main(String[] args) { WebSiteFactory webSiteFactory = new WebSiteFactory(); webSiteFactory.getWebSite("新闻").use(new User("lucy")); webSiteFactory.getWebSite("新闻").use(new User("jane")); webSiteFactory.getWebSite("博客").use(new User("jack")); webSiteFactory.getWebSite("博客").use(new User("maik")); webSiteFactory.getWebSite("博客").use(new User("seven")); System.out.println("网站类型共:" + webSiteFactory.countWebSiteType()); } } abstract class WebSite{ private String name; public abstract void use(User user); public void setName(String name) { this.name = name; } public String getName() { return name; } } class ConcreateWebSite extends WebSite { public ConcreateWebSite(String name) { super.setName(name); } @Override public void use(User user) { System.out.println("网站类型名称:" + super.getName() +"\t 网站用户:" + user.getUsername()); } } class User { private String username; public User(String username) { this.username = username; } public void setUsername(String username) { this.username = username; } public String getUsername() { return username; } } class WebSiteFactory{ private HashMap pool = new HashMap<>(); public WebSite getWebSite(String webSiteName){ if (!pool.containsKey(webSiteName)) { pool.put(webSiteName, new ConcreateWebSite(webSiteName)); } return pool.get(webSiteName); } public Integer countWebSiteType() { return pool.size(); } }
当系统中存在大量相同或者相似的对象时,享元模式是一种较好的解决方案,它通过共享技术实现相同或相似的细粒度对象的复用,从而节约了内存空间,提高了系统性能。相比其他结构型设计模式,享元模式的使用频率并不算太高,但是作为一种以“节约内存,提高性能”为出发点的设计模式,它在软件开发中还是得到了一定程度的应用。如:String
、Java的池技术等。
享元模式主要解决在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。
优点:
缺点:
使用场景:
代理模式,给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问。代理模式是为一个对象提供一个替身,以控制对这个对象的访问,即通过代理对象访问目标对象。这样做的好处是,可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象。代理模式有不同的形式,主要有三种 静态代理、动态代理 (JDK
代理、Cglib
代理)。
public class MainTest { public static void main(String[] args) { ProxyImpl proxy = new ProxyImpl(); StaticProxy staticProxy = new StaticProxy(proxy); staticProxy.test(); } } interface ProxyInterface { void test(); } class ProxyImpl implements ProxyInterface{ @Override public void test() { System.out.println("helloWorld"); } } class StaticProxy implements ProxyInterface{ private ProxyImpl target; public StaticProxy (ProxyImpl target){ this.target=target; } @Override public void test() { System.out.println("静态代理之前..."); target.test(); System.out.println("静态代理之后..."); } }
静态代理能在不修改目标对象的功能前提下,能通过代理对象对目标进行扩展。但是因为代理对象需要与目标对象实现相同的接口,所以会有很多代理类一旦接口增加方法后,目标对象与代理对象都需要维护。
JDK动态代理,代理对象不需要实现接口,但是目标对象需要实现接口,否则不能实现动态代理。代理对象的生产是利用JDK的API,动态的在内存中构建代理对象。
public class MainTest { public static void main(String[] args) { ProxyImpl proxy = new ProxyImpl(); ProxyInterface jdkProxyInterface = (ProxyInterface)new JDKProxy().bind(proxy); jdkProxyInterface.test(); } } interface ProxyInterface { void test(); } class ProxyImpl implements ProxyInterface{ @Override public void test() { System.out.println("helloWorld"); } } class JDKProxy { //通用类型,表示被代理的真实对象 private Object target; public Object bind(Object target){ this.target=target; //生成代理类(与被代理对象实现相同接口的兄弟类) return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), (proxy, method, args) -> { Object res; System.out.println("JDK动态代理前"); res=method.invoke(target, args); System.out.println("JDK动态代理后"); return res; }); } }
Cglib
代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展。Cglib
是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口,广泛的被许多AOP的框架使用,例如Spring AOP
,实现方法拦截。
以下测试代码需要导入Cglib
和asm
相关jar包:
asm-3.3.1.jar cglib-2.2.jar
使用Cglib
实现动态代理时出现了下面这个异常:
Exception in thread "main" java.lang.IncompatibleClassChangeError: class net.sf.cglib.core.DebuggingClassWriter has interface org.objectweb.asm.ClassVisitor as super class
原因是cglib.jar
包含asm.jar
包。报错内容是ClassVisitor
的父类不相容。详细原因分析。测试时用cglib2.2.jar
和asm3.3.1.jar
版本,解决jar包冲突问题。
public class MainTest { public static void main(String[] args) { Target target = new Target(); Target bind = (Target)new CGLibProxy().bind(target); bind.test(); } } class Target{ public void test() { System.out.println("helloWorld"); } } class CGLibProxy implements MethodInterceptor { private Object target; public Object bind(Object target) { this.target = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setCallback(this); return enhancer.create(); } @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object res; System.out.println("CGLib动态代理前"); res=method.invoke(target, args); System.out.println("CGLib动态代理后"); return res; } }
代理模式是常用的结构型设计模式之一,它为对象的间接访问提供了一个解决方案,可以对对象的访问进行控制。代理模式主要解决,在直接访问对象时带来的问题。比如说,要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因,对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问,直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
优点:
缺点:
使用场景:
职责链模式,避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。职责链模式是一种对象行为型模式。
public class MainTest { public static void main(String[] args) { // 产品 RequestEntity test = new RequestEntity("test", 2000); // 指定职责链 BeforeHandler before = new BeforeHandler("before"); AfterHandler after = new AfterHandler("after"); PostHandler post = new PostHandler("post"); // 形成链状闭环 要确保会被责任链中的组件处理 否则会一直循环下去 ,当然也可以选择不闭环 before.setHandler(after); after.setHandler(post); post.setHandler(before); after.handleRequest(test); } } class RequestEntity { public String name; public Integer grade; public RequestEntity(String name,Integer grade) { this.name = name; this.grade = grade; } public String getName() { return name; } public float getGrade() { return grade; } } abstract class Handler { // 下一个引用 protected Handler handler; protected String name; public Handler(String name) { this.name = name; } public void setHandler(Handler handler) { this.handler = handler; } public abstract void handleRequest(RequestEntity requestEntity); } class BeforeHandler extends Handler { public BeforeHandler(String name){ super(name); } @Override public void handleRequest(RequestEntity requestEntity) { if (requestEntity.getGrade() < 1000) { System.out.println("分数为:" + requestEntity.getGrade() + ",被" + this.name + "处理 "); }else { handler.handleRequest(requestEntity); } } } class AfterHandler extends Handler { public AfterHandler(String name){ super(name); } @Override public void handleRequest(RequestEntity requestEntity) { if (requestEntity.getGrade() <= 2000) { System.out.println("分数为:" + requestEntity.getGrade() + ",被" + this.name + "处理 "); }else { handler.handleRequest(requestEntity); } } } class PostHandler extends Handler { public PostHandler(String name){ super(name); } @Override public void handleRequest(RequestEntity requestEntity) { if (requestEntity.getGrade() > 3000) { System.out.println("分数为:" + requestEntity.getGrade() + ",被" + this.name + "处理 "); }else { handler.handleRequest(requestEntity); } } }
职责链模式通过建立一条链来组织请求的处理者,请求将沿着链进行传递,请求发送者无须知道请求在何时、何处以及如何被处理,实现了请求发送者与处理者的解耦。在软件开发中,如果遇到有多个对象可以处理同一请求时可以应用职责链模式,例如在Web应用开发中创建一个过滤器(Filter)链来对请求数据进行过滤,在工作流系统中实现公文的分级审批等等,使用职责链模式可以较好地解决此类问题。
优点:
缺点:
Handler
中设置一个最大节点数量,在setNext
方法中判断是否已经超过阀值,超过则不允许该链建立,避免出现超长链无意识地破坏系统性能。使用场景:
命令模式,将一个请求封装为一个对象,从而让我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。命令模式是一种对象行为型模式,其别名为动作模式或事务模式。命令模式可以将请求发送者和接收者完全解耦,发送者与接收者之间没有直接引用关系,发送请求的对象只需要知道如何发送请求,而不必知道如何完成请求。
public class MainTest { public static void main(String[] args) { LightReceiver lightReceiver = new LightReceiver(); LightOnCommand lightOnCommand = new LightOnCommand(lightReceiver); LightOffCommand lightOffCommand = new LightOffCommand(lightReceiver); RemoteCommand remoteCommand = new RemoteCommand(); // 测试 remoteCommand.setCommand(0,lightOnCommand,lightOffCommand); // 按下开灯按钮 System.out.println("按下开灯按钮 -------"); remoteCommand.onButtonPushed(0); // 按下关灯按钮 System.out.println("按下关灯按钮 -------"); remoteCommand.offButtonPushed(0); // 按下撤销按钮 System.out.println("按下撤销按钮 ---------"); remoteCommand.undoButtonPushed(0); } } interface Command{ /** * 执行命令 */ void exec(); /** * 撤销命令 */ void undo(); } class LightReceiver{ public void on(){ System.out.println("开灯 ..."); } public void off() { System.out.println("关灯 ..."); } } class LightOnCommand implements Command{ private LightReceiver lightReceiver; public LightOnCommand(LightReceiver lightReceiver) { super(); this.lightReceiver = lightReceiver; } @Override public void exec() { lightReceiver.on(); } @Override public void undo() { lightReceiver.off(); } } class LightOffCommand implements Command { private LightReceiver lightReceiver; public LightOffCommand(LightReceiver lightReceiver) { super(); this.lightReceiver = lightReceiver; } @Override public void exec() { lightReceiver.off(); } @Override public void undo() { lightReceiver.on(); } } /** * 空执行 默认命令实现类 */ class NoCommand implements Command { @Override public void exec() { System.out.println("默认命令执行"); } @Override public void undo() { System.out.println("默认撤销方法"); } } class RemoteCommand { // 存放开关命令 private Command onCommands[]; private Command offCommands[]; // 存放撤销命令 private Command undoCommands[]; public RemoteCommand() { undoCommands = new Command[5]; 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 onButtonPushed(int no) { onCommands[no].exec(); // 记录撤销操作 undoCommands[no] = onCommands[no]; } // 按下关闭的按钮 public void offButtonPushed(int no) { offCommands[no].exec(); // 记录撤销操作 undoCommands[no] = offCommands[no]; } // 按下撤销按钮 public void undoButtonPushed(int no) { undoCommands[no].undo(); } }
命令模式是一种使用频率非常高的设计模式,它可以将请求发送者与接收者解耦,请求发送者通过命令对象来间接引用请求接收者,使得系统具有更好的灵活性和可扩展性。在基于GUI的软件开发,无论是在电脑桌面应用还是在移动应用中,命令模式都得到了广泛的应用。在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。
优点:
缺点:
使用场景:
解释器模式,定义一个语言的文法,并且建立一个解释器来解释该语言中的句子,这里的“语言”是指使用规定格式和语法的代码。解释器模式是一种类行为型模式。
public class MainTest { public static void main(String[] args) throws IOException { String expStr = getExpStr(); HashMap var = getValue(expStr); Calculator calculator = new Calculator(expStr); System.out.println("运算结果:" + expStr + "=" + calculator.run(var)); } // 获得表达式 public static String getExpStr() throws IOException { System.out.print("请输入表达式:"); return (new BufferedReader(new InputStreamReader(System.in))).readLine(); } // 获得值映射 public static HashMap getValue(String expStr) throws IOException { HashMap map = new HashMap<>(); for (char ch : expStr.toCharArray()) { if (ch != '+' && ch != '-') { if (!map.containsKey(String.valueOf(ch))) { System.out.print("请输入" + String.valueOf(ch) + "的值:"); String in = (new BufferedReader(new InputStreamReader(System.in))).readLine(); map.put(String.valueOf(ch), Integer.valueOf(in)); } } } return map; } } abstract class AbstractExpression { /** * 表达式解释器 */ public abstract int interpreter(HashMap var); } /** * 终结表达式 */ class VarExpression extends AbstractExpression { private String key; public VarExpression(String key) { this.key = key; } @Override public int interpreter(HashMap map) { return map.get(key); } } /** * 非终结表达式 */ class SymbolExpression extends AbstractExpression { protected AbstractExpression left; protected AbstractExpression right; SymbolExpression(AbstractExpression left, AbstractExpression right) { this.left = left; this.right = right; } // 因为 SymbolExpression 是让其子类来实现,因此 interpreter 是一个默认实现 @Override public int interpreter(HashMap var) { return 0; } } /** * 减法 */ class SubExpression extends SymbolExpression { public SubExpression(AbstractExpression left, AbstractExpression right) { super(left, right); } @Override public int interpreter(HashMap var) { return super.left.interpreter(var) - super.right.interpreter(var); } } /** * 加法 */ class AddExpression extends SymbolExpression { public AddExpression(AbstractExpression left, AbstractExpression right) { super(left, right); } @Override public int interpreter(HashMap var) { return super.left.interpreter(var) + super.right.interpreter(var); } } /** * 计算器 调用加减法 */ class Calculator { private AbstractExpression expression; public Calculator(AbstractExpression expression) { this.expression = expression; } public Calculator(String expStr) { // 安排运算先后顺序 Stack stack = new Stack<>(); // 表达式拆分成字符数组 char[] charArray = expStr.toCharArray(); AbstractExpression left = null; AbstractExpression right = null; //遍历我们的字符数组, 即遍历 [a, +, b] //针对不同的情况,做处理 for (int i = 0; i < charArray.length; i++) { switch (charArray[i]) { case '+': // 从 stack 取 出 left => "a" left = stack.pop(); // 取出右表达式 "b" right = new VarExpression(String.valueOf(charArray[++i])); stack.push(new AddExpression(left, right)); // 然后根据得到 left 和 right 构建 AddExpresson 加入 stack break; case '-': left = stack.pop(); right = new VarExpression(String.valueOf(charArray[++i])); stack.push(new SubExpression(left, right)); break; default: //如果是一个 Var 就创建要给 VarExpression 对象,并 push 到 stack stack.push(new VarExpression(String.valueOf(charArray[i]))); break; } } //当遍历完整个 charArray 数组后,stack 就得到最后 this.expression = stack.pop(); } public int run(HashMap var) { //最后将表达式 a+b 和 var = {a=10,b=20} //然后传递给 expression 的 interpreter 进行解释执行 return this.expression.interpreter(var); } }
解释器模式为自定义语言的设计和实现提供了一种解决方案,它用于定义一组文法规则并通过这组文法规则来解释语言中的句子。虽然解释器模式的使用频率不是特别高,但是它在正则表达式、XML文档解释等领域还是得到了广泛使用。与解释器模式类似,目前还诞生了很多基于抽象语法树的源代码处理工具,例如Eclipse
中的Eclipse AST
,它可以用于表示Java语言的语法结构,用户可以通过扩展其功能,创建自己的文法规则。
优点:
缺点:
使用场景:
迭代器模式,提供一种方法来访问聚合对象,而不用暴露这个对象的内部表示,其别名为游标。迭代器模式是一种对象行为型模式。迭代器模式的重要用途就是帮助我们遍历容器。迭代器模式,提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知道集合对象的底层表示,即不暴露其内部的结构。在迭代器模式结构中包含聚合和迭代器两个层次结构,考虑到系统的灵活性和可扩展性,在迭代器模式中应用了工厂方法模式。
public class MainTest { public static void main(String[] args) { Bread bread = new Bread(); bread.add("面粉"); bread.add("黄油"); bread.add("白糖"); bread.add("鸡蛋"); Iterator iterator = bread.getIterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } } } class FoodIterator implements Iterator { String[] foods; int position = 0; public FoodIterator(String[] foods) { this.foods = foods; } @Override public boolean hasNext() { return position != foods.length; } @Override public Object next() { String food = foods[position]; position += 1; return food; } } interface Food { void add(String name); Iterator getIterator(); } class Bread implements Food{ private String[] foods = new String[4]; private int position = 0; @Override public void add(String name) { foods[position] = name; position += 1; } @Override public Iterator getIterator() { return new FoodIterator(this.foods); } }
迭代器模式是一种使用频率非常高的设计模式,通过引入迭代器可以将数据的遍历功能从聚合对象中分离出来,聚合对象只负责存储数据,而遍历数据由迭代器来完成。由于很多编程语言的类库都已经实现了迭代器模式,因此在实际开发中,我们只需要直接使用Java、C#等语言已定义好的迭代器即可,迭代器已经成为我们操作聚合对象的基本工具之一。迭代器的使用现在非常广泛,因为Java中提供了java.util.Iterator
。而且Java中的很多容器,如Collection
、Set
也都提供了对迭代器的支持。
优点:
缺点:
使用场景:
中介者模式,用一个中介对象(中介者)来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。中介者模式又称为调停者模式,它是一种对象行为型模式。如果在一个系统中对象之间存在多对多的相互关系,我们可以将对象之间的一些交互行为从各个对象中分离出来,并集中封装在一个中介者对象中,并由该中介者进行统一协调,这样对象之间多对多的复杂关系就转化为相对简单的一对多关系。通过引入中介者来简化对象之间的复杂交互,中介者模式是“迪米特法则”的一个典型应用。
public class MainTest { public static void main(String[] args) { //创建一个中介者对象 Mediator mediator = new ConcreteMediator(); //创建 Alarm 并且加入到ConcreteMediator 对象的 HashMap Alarm alarm = new Alarm(mediator, "alarm"); //创建了 CoffeeMachine 对象,并且加入到 ConcreteMediator 对象的 HashMap CoffeeMachine coffeeMachine = new CoffeeMachine(mediator, "coffeeMachine"); //创建 tV , 并 且加入到 ConcreteMediator 对象的 HashMap TV tV = new TV(mediator, "TV"); //让闹钟发出消息 依次调用 alarm.sendAlarm(0); coffeeMachine.finishCoffee(); alarm.sendAlarm(1); tV.startTv(); } } /** * 中介者 */ abstract class Mediator { public abstract void register(String colleagueName, Colleague colleague); public abstract void getMessage(int stateChange, String name); } class ConcreteMediator extends Mediator { /** * 集合,放入所有的同事对象 */ private HashMap colleagueMap; private HashMap interMap; public ConcreteMediator() { colleagueMap = new HashMap<>(); interMap = new HashMap<>(); } @Override public void register(String colleagueName, Colleague colleague) { if (colleague instanceof Alarm) { interMap.put("Alarm", colleagueName); } else if (colleague instanceof CoffeeMachine) { interMap.put("CoffeeMachine", colleagueName); } else { System.out.println("........"); } } @Override public void getMessage(int stateChange, String colleagueName) { if (colleagueMap.get(colleagueName) instanceof Alarm) { if (stateChange == 0) { ((CoffeeMachine) (colleagueMap.get(interMap .get("CoffeeMachine")))).startCoffee(); ((TV) (colleagueMap.get(interMap.get("TV")))).startTv(); } else if (stateChange == 1) { ((TV) (colleagueMap.get(interMap.get("TV")))).stopTv(); } } else if (colleagueMap.get(colleagueName) instanceof TV) { //如果 TV 发现消息 } } } /** * 抽象同事类 */ abstract class Colleague { private final Mediator mediator; public String name; public Colleague(Mediator mediator, String name) { this.mediator = mediator; this.name = name; } public Mediator getMediator() { return this.mediator; } public abstract void sendMessage(int stateChange); } class Alarm extends Colleague { public Alarm(Mediator mediator, String name) { super(mediator, name); //在创建 Alarm 同事对象时,将自己放入到 ConcreteMediator 对象中[集合] mediator.register(name, this); } public void sendAlarm(int stateChange) { this.sendMessage(stateChange); } @Override public void sendMessage(int stateChange) { // 调用的中介者对象的 getMessage 方法 this.getMediator().getMessage(stateChange, this.name); } } class TV extends Colleague { public TV(Mediator mediator, String name) { super(mediator, name); mediator.register(name, this); } @Override public void sendMessage(int stateChange) { this.getMediator().getMessage(stateChange, this.name); } public void startTv() { System.out.println("It's time to StartTv!"); } public void stopTv() { System.out.println("StopTv!"); } } class CoffeeMachine extends Colleague { public CoffeeMachine(Mediator mediator, String name) { super(mediator, name); mediator.register(name, this); } @Override public void sendMessage(int stateChange) { this.getMediator().getMessage(stateChange, this.name); } public void startCoffee() { System.out.println("It's time to startcoffee!"); } public void finishCoffee() { System.out.println("After 5 minutes!"); System.out.println("Coffee is ok!"); sendMessage(0); } }
中介者模式将一个网状的系统结构变成一个以中介者对象为中心的星形结构,在这个星型结构中,使用中介者对象与其他对象的一对多关系来取代原有对象之间的多对多关系。中介者模式在事件驱动类软件中应用较为广泛,特别是基于GUI(图形用户界面)的应用软件,此外,在类与类之间存在错综复杂的关联关系的系统中,中介者模式都能得到较好的应用。
优点:
缺点:
使用场景:
备忘录模式,在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。它是一种对象行为型模式,其别名为Token
。在设计备忘录类时需要考虑其封装性,除了Originator
类,不允许其他类来调用备忘录类Memento
的构造函数与相关方法,如果不考虑封装性,允许其他类调用构造方法,将导致在备忘录中保存的历史状态发生改变,通过撤销操作所恢复的状态就不再是真实的历史状态,备忘录模式也就失去了本身的意义。所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。
public class MainTest { public static void main(String[] args) { Originator originator = new Originator(); CareTaker careTaker = new CareTaker(); // 保存状态 careTaker.saveMemento(originator.saveState(" 状态#1 ")); careTaker.saveMemento(originator.saveState(" 状态#2 ")); careTaker.saveMemento(originator.saveState(" 状态#3 ")); System.out.println("目前保存的状态为:" + originator.getState()); System.out.println("开始恢复以前的状态 ...."); originator.recover(careTaker.recover(0)); System.out.println("恢复之后的状态为:" + originator.getState()); } } class Originator{ private String state; public String getState() { return state; } public Memento saveState(String state) { this.state = state; return new Memento(state); } public void recover(Memento memento) { this.state = memento.getState(); } } /** * 备忘录对象 保存对象信息 */ class Memento{ /** * 需要保存状态 */ private final String state; public Memento(String state) { this.state = state; } public String getState() { return state; } } /** * 管理备忘录对象 */ class CareTaker { public ArrayList mementos = new ArrayList<>(); public Memento recover(int index) { return mementos.get(index); } public void saveMemento(Memento memento) { mementos.add(memento); } }
备忘录模式在很多软件的使用过程中普遍存在,但是在应用软件开发中,它的使用频率并不太高,因为现在很多基于窗体和浏览器的应用软件并没有提供撤销操作。如果需要为软件提供撤销功能,备忘录模式无疑是一种很好的解决方案。在一些字处理软件、图像编辑软件、数据库管理系统等软件中备忘录模式都得到了很好的应用。为了符合迪米特原则,还要增加一个管理备忘录的类(CareTaker
),为了节约内存,可使用原型模式和备忘录模式。
优点:
缺点:
使用场景:
观察者模式,定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式的别名包括发布-订阅模式、模型-视图模式、源-监听器模式或从属者模式。观察者模式是一种对象行为型模式。
观察者模式描述了如何建立对象与对象之间的依赖关系,以及如何构造满足这种需求的系统。观察者模式包含观察目标和观察者两类对象,一个目标可以有任意数目的与之相依赖的观察者,一旦观察目标的状态发生改变,所有的观察者都将得到通知。作为对这个通知的响应,每个观察者都将监视观察目标的状态以使其状态与目标状态同步,这种交互也称为发布-订阅(Publish-Subscribe)。观察目标是通知的发布者,它发出通知时并不需要知道谁是它的观察者,可以有任意数目的观察者订阅它并接收通知。
public class MainTest { public static void main(String[] args) { WeatherData weatherData = new WeatherData(); CurrentCondition currentCondition = new CurrentCondition(); BaiduSite baiduSite = new BaiduSite(); // 注册观察者 weatherData.registerObserver(currentCondition); weatherData.registerObserver(baiduSite); // 设置数据 一旦数据变化 所有的观察者都会变化 weatherData.setWeatherData(10f, 20f); // 移除注册观察者 weatherData.removeObserver(baiduSite); // 唤醒所有已经注册的观察者 weatherData.notifyObservers(); } } interface Subject { void registerObserver(Observer observer); void removeObserver(Observer observer); void notifyObservers(); } /** * 观察者 */ interface Observer { void update(float temperature, float humidity); } class WeatherData implements Subject{ private ArrayList observers = new ArrayList<>(); private float temperature; private float humidity; public void setWeatherData(float humidity, float temperature) { this.humidity = humidity; this.temperature = temperature; } @Override public void registerObserver(Observer observer) { observers.add(observer); } @Override public void removeObserver(Observer observer) { if (observers.contains(observer)) { observers.remove(observer); } } @Override public void notifyObservers() { // 唤醒所有的观察者 for (Observer observer : observers) { observer.update(temperature,humidity); } } } class CurrentCondition implements Observer{ private float temperature; private float humidity; @Override public void update(float temperature, float humidity) { this.temperature = temperature; this.humidity = humidity; displayed(); } void displayed() { System.out.println("===当前天气情况==="); System.out.println("当前湿度:" + this.temperature); System.out.println("当前温度:" + this.humidity); } } class BaiduSite implements Observer{ private float temperature; private float humidity; @Override public void update(float temperature, float humidity) { this.temperature = temperature; this.humidity = humidity; displayed(); } void displayed() { System.out.println("===当前百度网站天气情况==="); System.out.println("当前湿度:" + this.temperature); System.out.println("当前温度:" + this.humidity); } }
观察者模式是一种使用频率非常高的设计模式,无论是移动应用、Web应用或者桌面应用,观察者模式几乎无处不在,它为实现对象之间的联动提供了一套完整的解决方案,凡是涉及到一对一或者一对多的对象交互场景都可以使用观察者模式。观察者模式广泛应用于各种编程语言的GUI事件处理的实现,在基于事件的XML解析技术(如SAX2)以及Web事件处理中也都使用了观察者模式。
优点:
缺点:
使用场景:
注意事项:
状态模式,允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。其别名为状态对象,状态模式是一种对象行为型模式。状态模式用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题。当系统中某个对象存在多个状态,这些状态之间可以进行转换,而且对象在不同状态下行为不相同时可以使用状态模式。状态模式将一个对象的状态从该对象中分离出来,封装到专门的状态类中,使得对象状态可以灵活变化,对于客户端而言,无须关心对象状态的转换以及对象所处的当前状态,无论对于何种状态的对象,客户端都可以一致处理。
public class MainTest { public static void main(String[] args) { // 创建活动对象,奖品有 1 个奖品 RaffleActivity activity = new RaffleActivity(1); // 我们连续抽 300 次奖 for (int i = 0; i < 30; i++) { System.out.println("--------第" + (i + 1) + "次抽奖----------"); // 参加抽奖,第一步点击扣除积分 activity.debuctMoney(); // 第二步抽奖 activity.raffle(); } } } abstract class State { // 扣除积分 - 50 public abstract void deductMoney(); // 是否抽中奖品 public abstract boolean raffle(); // 发放奖品 public abstract void dispensePrize(); } class RaffleActivity { // state 表示活动当前的状态,是变化 State state = null; // 奖品数量 int count = 0; // 四个属性,表示四种状态 State noRafflleState = new NoRaffleState(this); State canRaffleState = new CanRaffleState(this); State dispenseState = new DispenseState(this); State dispensOutState = new DispenseOutState(this); //构造器 //1. 初始化当前的状态为 noRafflleState(即不能抽奖的状态) //2. 初始化奖品的数量 public RaffleActivity(int count) { this.state = getNoRafflleState(); this.count = count; } //扣分, 调用当前状态的 deductMoney public void debuctMoney() { state.deductMoney(); } //抽奖 public void raffle() { // 如果当前的状态是抽奖成功 if (state.raffle()) { //领取奖品 state.dispensePrize(); } } public State getState() { return state; } public void setState(State state) { this.state = state; } //这里请大家注意,每领取一次奖品,count-- public int getCount() { int curCount = count; count--; return curCount; } public void setCount(int count) { this.count = count; } public State getNoRafflleState() { return noRafflleState; } public void setNoRafflleState(State noRafflleState) { this.noRafflleState = noRafflleState; } public State getCanRaffleState() { return canRaffleState; } public void setCanRaffleState(State canRaffleState) { this.canRaffleState = canRaffleState; } public State getDispenseState() { return dispenseState; } public void setDispenseState(State dispenseState) { this.dispenseState = dispenseState; } public State getDispensOutState() { return dispensOutState; } public void setDispensOutState(State dispensOutState) { this.dispensOutState = dispensOutState; } } class DispenseOutState extends State { // 初始化时传入活动引用 RaffleActivity activity; public DispenseOutState(RaffleActivity activity) { this.activity = activity; } @Override public void deductMoney() { System.out.println("奖品发送完了,请下次再参加"); } @Override public boolean raffle() { System.out.println("奖品发送完了,请下次再参加"); return false; } @Override public void dispensePrize() { System.out.println("奖品发送完了,请下次再参加"); } } class DispenseState extends State { // 初始化时传入活动引用,发放奖品后改变其状态 RaffleActivity activity; public DispenseState(RaffleActivity activity) { this.activity = activity; } @Override public void deductMoney() { System.out.println("不能扣除积分"); } @Override public boolean raffle() { System.out.println("不能抽奖"); return false; } //发放奖品 @Override public void dispensePrize() { if (activity.getCount() > 0) { System.out.println("恭喜中奖了"); // 改变状态为不能抽奖 activity.setState(activity.getNoRafflleState()); } else { System.out.println("很遗憾,奖品发送完了"); // 改变状态为奖品发送完毕, 后面我们就不可以抽奖 activity.setState(activity.getDispensOutState()); //System.out.println("抽奖活动结束"); //System.exit(0); } } } class NoRaffleState extends State { // 初始化时传入活动引用,扣除积分后改变其状态 RaffleActivity activity; public NoRaffleState(RaffleActivity activity) { this.activity = activity; } // 当前状态可以扣积分 , 扣除后,将状态设置成可以抽奖状态 @Override public void deductMoney() { System.out.println("扣除 50 积分成功,您可以抽奖了"); activity.setState(activity.getCanRaffleState()); } // 当前状态不能抽奖 @Override public boolean raffle() { System.out.println("扣了积分才能抽奖喔!"); return false; } // 当前状态不能发奖品 @Override public void dispensePrize() { System.out.println("不能发放奖品"); } } class CanRaffleState extends State { RaffleActivity activity; public CanRaffleState(RaffleActivity activity) { this.activity = activity; } @Override public void deductMoney() { System.out.println("已经扣取过了积分"); } //可以抽奖, 抽完奖后,根据实际情况,改成新的状态 @Override public boolean raffle() { System.out.println("正在抽奖,请稍等!"); Random r = new Random(); int num = r.nextInt(10); // 10%中奖机会 if (num == 0) { // 改变活动状态为发放奖品 activity.setState(activity.getDispenseState()); return true; } else { System.out.println("很遗憾没有抽中奖品!"); // 改变状态为不能抽奖 activity.setState(activity.getNoRafflleState()); return false; } } // 不能发放奖品 @Override public void dispensePrize() { System.out.println("没中奖,不能发放奖品"); } }
状态模式将一个对象在不同状态下的不同行为封装在一个个状态类中,通过设置不同的状态对象可以让环境对象拥有不同的行为,而状态转换的细节对于客户端而言是透明的,方便了客户端的使用。在实际开发中,状态模式具有较高的使用频率,在工作流和游戏开发中状态模式都得到了广泛的应用,例如公文状态的转换、游戏中角色的升级等。
优点:
缺点:
使用场景:
策略模式,定义一系列算法类,将每一个算法封装起来,并让它们可以相互替换,策略模式让算法独立于使用它的客户而变化,也称为政策模式。策略模式是一种对象行为型模式。策略模式的主要目的是将算法的定义与使用分开,也就是将算法的行为和环境分开,将算法的定义放在专门的策略类中,每一个策略类封装了一种实现算法,使用算法的环境类针对抽象策略类进行编程,符合“依赖倒转原则”。在出现新的算法时,只需要增加一个新的实现了抽象策略类的具体策略类即可。
public class MainTest { public static void main(String[] args) { Bird bird = new Bird(); bird.fly(); Duck duck = new Duck(); duck.fly(); Dog dog = new Dog(); dog.fly(); System.out.println("变为会飞 :"); dog.setFlyStrategy(new GoodFlyStrategy()); dog.fly(); } } abstract class AbstractStrategy { protected FlyStrategy flyStrategy; public void setFlyStrategy(FlyStrategy flyStrategy) { this.flyStrategy = flyStrategy; } public abstract void fly(); } class Bird extends AbstractStrategy{ public Bird() { System.out.print("小鸟"); flyStrategy = new GoodFlyStrategy(); } @Override public void fly() { flyStrategy.fly(); } } class Duck extends AbstractStrategy { public Duck() { System.out.print("鸭子"); flyStrategy = new BadFlyStrategy(); } @Override public void fly() { flyStrategy.fly(); } } class Dog extends AbstractStrategy { public Dog() { System.out.print("狗"); flyStrategy = new NoFlyStrategy(); } @Override public void fly() { flyStrategy.fly(); } } interface FlyStrategy { void fly(); } class GoodFlyStrategy implements FlyStrategy { @Override public void fly() { System.out.println("擅长飞翔 ..."); } } class BadFlyStrategy implements FlyStrategy { @Override public void fly() { System.out.println("不擅长飞翔 ..."); } } class NoFlyStrategy implements FlyStrategy { @Override public void fly() { System.out.println("不会飞 ..."); } }
策略模式用于算法的自由切换和扩展,它是应用较为广泛的设计模式之一。策略模式对应于解决某一问题的一个算法族,允许用户从该算法族中任选一个算法来解决某一问题,同时可以方便地更换算法或者增加新的算法。只要涉及到算法的封装、复用和切换都可以考虑使用策略模式。如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。
优点:
缺点:
使用场景:
模板方法模式,定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。模板方法模式是一种基于继承的代码复用技术,它是一种类行为型模式。
public class MainTest { public static void main(String[] args) { System.out.println("=====红豆豆浆====="); RedBean redBean = new RedBean(); redBean.template(); System.out.println("=====花生豆浆====="); Peanut peanut = new Peanut(); peanut.template(); System.out.println("=====豆浆====="); None none = new None(); none.template(); } } abstract class SoyaMilk { final void template(){ filterMaterial(); soak(); if (isAppended()) { add(); } over(); } void filterMaterial() { System.out.println("第一步:筛选材料"); } void soak() { System.out.println("第二步:浸泡"); } abstract void add(); void over(){ System.out.println("第四步:打豆浆"); } /** * 钩子方法 * @return */ boolean isAppended(){ return true; } } class Peanut extends SoyaMilk{ @Override void add() { System.out.println("第三步:加入花生"); } } class RedBean extends SoyaMilk { @Override void add() { System.out.println("第三步:加入红豆"); } } class None extends SoyaMilk { @Override void add() { } @Override boolean isAppended() { return false; } }
模板方法模式是基于继承的代码复用技术,它体现了面向对象的诸多重要思想,是一种使用较为频繁的模式。模板方法模式广泛应用于框架设计中,以确保通过父类来控制处理流程的逻辑顺序(如框架的初始化,测试流程的设置等)。
优点:
缺点:
使用场景:
访问者模式,提供一个作用于某对象结构中的各元素的操作表示,它使我们可以在不改变各元素的类的前提下定义作用于这些元素的新操作。访问者模式是一种对象行为型模式。
public class MainTest { public static void main(String[] args) { ObjectStructure objectStructure = new ObjectStructure(); objectStructure.attach(new Man()); objectStructure.attach(new WoMan()); objectStructure.attach(new Man()); objectStructure.attach(new WoMan()); // 显示成功的评价 Success success = new Success(); objectStructure.display(success); System.out.println("=================="); // 显示失败的评价 Fail fail = new Fail(); objectStructure.display(fail); } } abstract class Action { protected abstract void getManResult(Man man); protected abstract void getWomanResult(WoMan woman ); } class Success extends Action{ @Override protected void getManResult(Man man) { System.out.println("男人觉得很赞~"); } @Override protected void getWomanResult(WoMan woman) { System.out.println("女人觉得很赞~"); } } class Fail extends Action{ @Override protected void getManResult(Man man) { System.out.println("男人觉得很失败~"); } @Override protected void getWomanResult(WoMan woman) { System.out.println("女人觉得很失败~"); } } abstract class Person { abstract void accpet(Action action); } class WoMan extends Person{ @Override void accpet(Action action) { action.getWomanResult(this); } } class Man extends Person{ @Override void accpet(Action action) { action.getManResult(this); } } class ObjectStructure { ArrayList people = new ArrayList<>(); public void attach(Person person) { people.add(person); } public void detach(Person person) { people.remove(person); } public void display(Action acion) { people.forEach(item -> { item.accpet(acion); }); } }
由于访问者模式的使用条件较为苛刻,本身结构也较为复杂,因此在实际应用中使用频率不是特别高。当系统中存在一个较为复杂的对象结构,且不同访问者对其所采取的操作也不相同时,可以考虑使用访问者模式进行设计。在XML文档解析、编译器的设计、复杂集合对象的处理等领域访问者模式得到了一定的应用。
优点:
缺点:
使用场景: