设计模式
1.设计模式的定义
- 1.设计模式(
Design Pattern
):是对代码开发经验的总结,是解决特定问题的一系列方案- 2.其不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案
2.设计模式的六大原则
开闭原则(Open Closed Principle)
- 1.一个软件实体(类,模块和函数)应该对扩展开放,对修改关闭
- 2.即程序需要进行拓展的时候,不能去修改原有的代码,而是要扩展原有代码,从而实现一个热插拔的效果
- 3.
目的
:为了使程序的扩展性好,易于维护和升级- 4.
实现
:使用接口和抽象类等
1.单一职责原则(Single Responsibility Principle)
- 1.一个类应该只有一个发生变化的原因,不要存在多于一个导致类变更的原因
- 2.即每个类应该实现单一的职责,否则就应该把类拆分
2.里氏替换原则(Liskov Substitution Principle)
- 1.所有引用
基类
的地方必须能透明地使用其子类
的对象- 2.任何基类可以出现的地方子类一定可以出现,
里氏替换原则
是继承复用的基石,只有当子类可以替换基类,软件单位的功能不受到影响时,基类才能真正被复用,而子类也能够在基类的基础上增加新的行为- 3.
里氏代换原则
是对开闭原则
的补充,实现开闭原则
的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏替换原则
是对实现抽象化具体步骤的规范- 4.
里氏替换原则
中子类对父类的方法尽量不要重写和重载,因为父类代表了定义好的结构,通过这个规范的接口与外界交互,子类不应该随便破坏它
3.依赖倒置原则(Dependence Inversion Principle)
- 1.上层模块不应该依赖底层模块,而应该依赖于抽象
- 2.抽象不应该依赖于细节,细节应该依赖于抽象
- 3.即面向接口编程,依赖于抽象而不依赖于具体类,写代码时用到具体类时,不与具体类交互而是与具体类的上层接口交互
4.接口隔离原则(Interface Segregation Principle)
- 1.实体类不应该依赖它不需要的接口
- 2.类间的依赖关系应该建立在最小的接口上
- 3.每个接口中不存在子类用不到却必须实现的方法,否则将接口拆分,使用多个隔离的接口,比使用单个接口(多个接口方法集合到一个的接口)要好
5.迪米特法则(最少知道原则)(Law of Demeter)
- 1.一个类对自己依赖的类知道的越少越好,无论被依赖的类多么复杂,都应该将逻辑封装在方法的内部,通过
public
方法提供给外部,这样当被依赖的类变化时才能最小的影响该类- 2.即一个实体应当尽量少的与其他实体之间发生相互作用使得系统功能模块相对独立
6.合成复用原则(Composite Reuse Principle)
- 1.尽量使用对象组合/聚合,而不是继承关系达到软件复用的目的
- 2.合成或聚合可以将已有对象纳入到新对象中使之成为新对象的一部分,因此新对象可以调用已有对象的功能
3.设计模式的三大类
1.创建型模式(Creational Pattern)
- 1.对类的实例化过程进行了抽象,能够将软件模块中对象的创建和对象的使用分离(
5
种)
- 1.
工厂模式
- 2.
原型模式
- 3.
单例模式
- 4.
建造者模式
- 5.
抽象工厂模式
- 2.记忆口诀:创工原单建抽(创公园,但见愁)
1.工厂模式(Factory Pattern)
- 1.定义一个创建对象的接口,让其子类决定实例化具体的工厂类,工厂模式使其创建过程延迟到子类进行
- 2.创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象
1.普通工厂模式
- 1.建立一个工厂类并对实现了同一接口的子类进行实例的创建
//例:发送邮件和短信 //1.首先,创建二者的共同接口 public interface Sender { public void Send(); } //2.其次,创建实现类 public class MailSender implements Sender { @Override public void Send() { System.out.println("this is mailsender!"); } } public class SmsSender implements Sender { @Override public void Send() { System.out.println("this is sms sender!"); } } //最后,建工厂类 public class SendFactory { public Sender produce(String type) { if ("mail".equals(type)) { return new MailSender(); } else if ("sms".equals(type)) { return new SmsSender(); } else { System.out.println("请输入正确的类型!"); return null; } } } //测试 public class FactoryTest { public static void main(String[] args) { SendFactory factory = new SendFactory(); Sender sender = factory.produce("sms"); sender.Send(); } }
2.多工厂模式
- 1.普通工厂模式中如果传递的字符串出错则不能正确创建对象
- 2.多工厂模式是对普通工厂模式的改进,提供多个工厂方法分别创建对象
//例:发送邮件和短信 //1.首先,创建二者的共同接口 public interface Sender { public void Send(); } //2.其次,创建实现类 public class MailSender implements Sender { @Override public void Send() { System.out.println("this is mailsender!"); } } public class SmsSender implements Sender { @Override public void Send() { System.out.println("this is sms sender!"); } } //最后,建工厂类 public class SendFactory { public Sender produceMail(){ return new MailSender(); } public Sender produceSms(){ return new SmsSender(); } } //测试 public class FactoryTest { public static void main(String[] args) { SendFactory factory = new SendFactory(); Sender sender = factory.produceMail(); sender.Send(); } }
3.静态工厂方法模式
- 1.将多工厂模式中的方法置为静态的,不需要创建实例就可以直接调用,并将其构造方法私有
//例:发送邮件和短信 //1.首先,创建二者的共同接口 public interface Sender { public void Send(); } //2.其次,创建实现类 public class MailSender implements Sender { @Override public void Send() { System.out.println("this is mailsender!"); } } public class SmsSender implements Sender { @Override public void Send() { System.out.println("this is sms sender!"); } } //最后,建工厂类 public class SendFactory { public static Sender produceMail(){ return new MailSender(); } public static Sender produceSms(){ return new SmsSender(); } private SendFactory(){}; } //测试 public class FactoryTest { public static void main(String[] args) { Sender sender = SendFactory.produceMail(); sender.Send(); } }
- 1.
工厂模式应用场景
:需要创建大量的对象- 2.
工厂模式采用方案
:以上三种模式,一般采用静态工厂方法模式- 3.
工厂模式实际案例
:
- 1.
Executors
类中返回线程池的方法
2.原型模式(Prototype Pattern)
- 1.该模式的是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象
- 2.原型模式实现克隆操作,需要实现
Cloneable
接口并重写clone()
抽象方法- 3.深拷贝和浅拷贝:可参考
JAVA SE
文章中的深拷贝和浅拷贝
//抽象类实现克隆接口(Cloneable) public abstract class Shape implements Cloneable { private String id; protected String type; abstract void draw(); public String getType(){ return type; } public String getId() { return id; } public void setId(String id) { this.id = id; } public Object clone() { Object clone = null; try { clone = super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return clone; } } //扩展抽象类的实体类 public class Rectangle extends Shape { public Rectangle(){ type = "Rectangle"; } @Override public void draw() { System.out.println("Inside Rectangle::draw() method."); } } public class Square extends Shape { public Square(){ type = "Square"; } @Override public void draw() { System.out.println("Inside Square::draw() method."); } } public class Circle extends Shape { public Circle(){ type = "Circle"; } @Override public void draw() { System.out.println("Inside Circle::draw() method."); } } //创建一个类,从数据库获取实体类,并把它们存储在一个 Hashtable 中。 import java.util.Hashtable; public class ShapeCache { private static Hashtable<String, Shape> shapeMap = new Hashtable<String, Shape>(); public static Shape getShape(String shapeId) { Shape cachedShape = shapeMap.get(shapeId); return (Shape) cachedShape.clone(); } // 对每种形状都运行数据库查询,并创建该形状 // shapeMap.put(shapeKey, shape); // 例如,我们要添加三种形状 public static void loadCache() { Circle circle = new Circle(); circle.setId("1"); shapeMap.put(circle.getId(),circle); Square square = new Square(); square.setId("2"); shapeMap.put(square.getId(),square); Rectangle rectangle = new Rectangle(); rectangle.setId("3"); shapeMap.put(rectangle.getId(),rectangle); } } //使用ShapeCache 类来获取存储在 Hashtable 中的形状的克隆。 public class PrototypePatternDemo { public static void main(String[] args) { ShapeCache.loadCache(); Shape clonedShape = (Shape) ShapeCache.getShape("1"); System.out.println("Shape : " + clonedShape.getType()); Shape clonedShape2 = (Shape) ShapeCache.getShape("2"); System.out.println("Shape : " + clonedShape2.getType()); Shape clonedShape3 = (Shape) ShapeCache.getShape("3"); System.out.println("Shape : " + clonedShape3.getType()); } }
- 1.
原型模式应用场景
:需要创建复杂对象或生成大量同一类型对象的时候可以使用原型模式,可以简化对象的创建过程- 2.
原型模式实际案例
:
- 1.
Spring
中原型Bean
的创建
3.单例模式(Singleton Pattern)
- 1.
单例模式
涉及一个单一的类,该类负责创建自己的对象同时确保只有单个对象被创建- 2.
单例模式
提供了一种访问其唯一对象的方式,可以直接访问不需要实例化该类的对象- 3.
单例模式
保证了程序中只有一个实例存在并且能全局访问- 4.注意
- 1.单例类只能有一个实例
- 2.单例类必须自己创建自己的唯一实例
- 3.单例类必须给所有其他对象提供这一实例
- 5.优点
- 1.内存中只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例
- 2.避免对资源的多重占用
- 6.缺点
- 1.没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化
- 7.作用:保证一个类仅有一个实例,并提供一个访问它的全局访问点
- 8.关键:
构造函数私有化
public class SingleObject { //创建 SingleObject 的一个对象 private static SingleObject instance = new SingleObject(); //让构造函数为 private,这样该类就不会被实例化 private SingleObject(){} //获取唯一可用的对象 public static SingleObject getInstance(){ return instance; } public void showMessage(){ System.out.println("Hello World!"); } } public class SingletonPatternDemo { public static void main(String[] args) { //不合法的构造函数 //编译时错误:构造函数 SingleObject() 是不可见的 //SingleObject object = new SingleObject(); //获取唯一可用的对象 SingleObject object = SingleObject.getInstance(); //显示消息 object.showMessage(); } }
1.懒汉式1
- 1.
懒汉式
是最基本的实现方式,并没有在加载时就创建单例对象,而是在使用时才去进行懒加载- 2.该方式最大的问题是不支持多线程,因为没有加锁,所以严格意义上并不算单例模式,一般使用适用于单线程
public class Singleton { private static Singleton instance; private Singleton (){} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
2.懒汉式2
- 1.该方式线程安全,能应用于多线程中,但是效率低,因为通过
synchronized
加锁- 2.优点:第一次调用才初始化,避免内存浪费。
- 3.缺点:必须通过
synchronized
加锁才能保证多线程下单例,但加锁会影响效率public class Singleton { private static Singleton instance; private Singleton (){} public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
3.饿汉式
- 1.
饿汉式
也是最基本的实现方式,在类加载时就创建唯一对象,容易产生垃圾对象浪费内存- 2.优点:基于
类加载机制
(可参考JVM
文章中的类加载机制
)避免了多线程的同步问题,不需要加锁,执行效率会提高- 3.缺点:类加载时就初始化,浪费内存
public class Singleton { private static Singleton instance = new Singleton(); private Singleton (){} public static Singleton getInstance() { return instance; } }
4.双重校验锁(double-checked locking,DCL)
- 1.上述
饿汉式
不能延时加载,懒汉式
有性能问题,而双重检测方式既支持延迟加载,又支持高并发- 2.当
instance
对象被创建后,再次调getInstance
方法不再会进入synchronized
加锁的代码之中- 3.优点:资源利用率高,第一次执行
getInstance
时才会被实例化,并且只有第一次执行才会进行加锁代码中,效率高- 4.缺点:第一次加载反应稍慢
public class Singleton { private static Singleton instance; private Singleton (){} public static Singleton getInstance() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return instance; } }
5.静态内部类
- 1.
静态内部类
能达到和双重校验锁一样的功效,但实现更简单- 2.该方式同样利用
类加载机制
避免了多线程的同步问题- 3.第
3
种方式只要Singleton
类被加载,那么instance
就会被实例化,而静态内部类中即使Singleton
类被加载,instance
也不会立即被初始化,因为SingletonHolder
类没有被主动使用,只有通过显式调用getInstance
方法时才会显式加载SingletonHolder
类从而实例化instance
- 4.优点:不仅能保证线程安全,也能够保证单例对象唯一,同时也延迟了单例的实例化,而且第一次加载不会慢
public class Singleton { private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton (){} public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } }
6.枚举
- 1.
枚举
是单例最简单的实现方式,这种实现方式通过Java
枚举类型本身的特性,保证了实例创建的线程安全性和实例的唯一性- 2.该方式是
Josh Bloch
提倡的方式,不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化public enum Singleton { INSTANCE; public void whateverMethod() { } }
- 1.
单例模式应用场景
:只存在一个实例且实例对象需要频繁被访问或一个全局使用的类频繁地创建与销毁- 2.
单例模式采用方案
- 1.以上总结为
五
种模式,一般情况下不建议使用
第1
种和第2
种懒汉方式,建议使用
第3
种饿汉方式- 2.只有在要明确实现懒加载效果时才会使用第
5
种静态内部类方式实现懒汉式加载- 3.如果涉及到
反序列化
创建对象时,可以使用第6
种枚举方式,如果有其他特殊的需求,可以考虑使用第4
种双检锁方式- 3.
单例模式实际案例
:
- 1.
Java
中的Runtime
类
- 2.
Spring Bean
采用了双重校验锁以及ConcurrentHashMap
作为容器实现了单例设计,并且通过三级缓存
解决循环依赖的问题
- 3.网站的计数器
4.建造者模式(Builder Pattern)
- 1.
建造者模式
是按照指定的步骤一步一步构建复杂对象- 2.使用
建造者模式
将原本复杂的对象创建过程按照规律分解成多个小步骤,构建对象时可以灵活的选择或修改步骤从而构建出不同的对象- 3.调用者只需要传入需要构建的类型便能够得到需要的对象,并不需要关心创建的过程从而实现解耦
- 4.优点
- 1.
建造者模式
将类的构造函数中的可选参数分离出来,使用setter
的方式进行初始化,非常灵活- 2.
建造者模式
是链式调用,属性连续设置,当调用build()
方法实例化对象后对象不可再进行改变,看起来是一个整体,较简洁,易于程序员阅读和管理- 5.缺点: 由于构造函数私有,使用
build
方法构造对象后对象不可变- 6.注意:与工厂模式的区别是:建造者模式更加关注与零件装配的顺序
- 7.实现步骤
- 1.需构建类中中创建一个静态内部类
Builder
,然后将构建类中的参数都复制到Builder
类中- 2.需构建类中创建一个
private
的构造函数,参数为Builder
类型- 3.
Builder
中创建一个public
的构造函数,参数为需构建类中必填的参数- 4.
Builder
中创建setter
函数,对需构建类中可选参数进行赋值,返回值为Builder
类型的实例- 5.
Builder
中创建一个build()
方法并在其中构建需构建类的实例(参数为Builder
当前对象)并返回package com.redis.entity; import lombok.Data; @Data public class Computer { private final String cpu;//必须 private final String ram;//必须 private final int usbCount;//可选 private final String keyboard;//可选 private final String display;//可选 private Computer(Builder builder){ this.cpu=builder.cpu; this.ram=builder.ram; this.usbCount=builder.usbCount; this.keyboard=builder.keyboard; this.display=builder.display; } public static class Builder{ private String cpu;//必须 private String ram;//必须 private int usbCount;//可选 private String keyboard;//可选 private String display;//可选 public Builder(String cup,String ram){ this.cpu=cup; this.ram=ram; } public Builder setUsbCount(int usbCount) { this.usbCount = usbCount; return this; } public Builder setKeyboard(String keyboard) { this.keyboard = keyboard; return this; } public Builder setDisplay(String display) { this.display = display; return this; } public Computer build(){ return new Computer(this); } } //省略getter方法 }
- 1.
建造者模式应用场景
:需要生成的对象具有复杂的内部结构,并且对象中的复杂组件的调用和赋值过程能够自定义- 2.
建造者模式实际案例
:
- 1.
Lombok
中的@Builder
注解,使用该注解的类不用再new
对象,直接赋值然后调用build()
方法便能构建对象- 2.其原理就是在使用该注解的类中生成一个静态内部类
Builder
,然后通过调用该内部类的方法给生成的类对象赋值
5.抽象工厂模式(Abstract Factory)
- 1.工厂模式的问题:类的创建依赖工厂类,如果想要拓展程序,必须对工厂类进行修改,违背了开闭原则
- 2.所以从设计角度考虑需要用到
抽象工厂模式
,创建多个工厂类,一旦需要增加新的功能直接增加新的工厂类即可,不需要修改之前的代码- 3.优点:增加一种功能只需做写一个实现类,实现对应类接口,同时实现一个工厂类,实现对应工厂接口,而无需去改动现有的代码,遵循开闭原则
- 4.缺点:由于采用该模式需要引入更多的接口和类,代码可能会比之前更加复杂
//实现类的接口 public interface Sender { public void Send(); } //两个实现类 public class MailSender implements Sender { @Override public void Send() { System.out.println("this is mailsender!"); } } public class SmsSender implements Sender { @Override public void Send() { System.out.println("this is sms sender!"); } } //两个工厂类 public class SendMailFactory implements Provider { @Override public Sender produce(){ return new MailSender(); } } public class SendSmsFactory implements Provider{ @Override public Sender produce() { return new SmsSender(); } } //工厂类的接口 public interface Provider { public Sender produce(); } public class Test { public static void main(String[] args) { Provider provider = new SendMailFactory(); Sender sender = provider.produce(); sender.Send(); } }
- 1.
抽象工厂模式应用场景
:系统的产品有多于一个的产品族,而系统只消费其中某一族的产品- 2.
抽象工厂模式实际案例
:
- 1.
Java
中的Connection
,具体可参考https://blog.csdn.net/qq_23633427/article/details/107304244
2.结构型模式(Structural Pattern)
- 1.
结构型模式
关注对象的组成以及对象之间的依赖关系,描述如何将类或对象结合在一起形成更大的结构(7
种)
- 1.
享元模式
- 2.
外观模式
- 3.
组合模式
- 4.
适配器模式
- 5.
装饰器模式
- 6.
代理模式
- 7.
桥接模式
- 2.记忆口诀:结享外组适代装桥(姐想外租,世代装桥)
1.享元模式(Flyweight Pattern)
- 1.
享元模式
主要用于减少创建对象的数量,以减少内存占用和提高性能- 2.
享元模式
尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象- 3.大量对象存在时可能会造成内存溢出,把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建
- 4.优点:大大减少对象的创建,降低系统的内存,提高效率
- 5.缺点:提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱
- 6.注意:需要用唯一标识码判断是否已存在,如果已存在则返回这个唯一标识码所标识的对象,一般采用
HashMap
存储这些对象//创建一个接口。 public interface Shape { void draw(); } //创建实现接口的实体类。 public class Circle implements Shape { private String color; private int x; private int y; private int radius; public Circle(String color){ this.color = color; } public void setX(int x) { this.x = x; } public void setY(int y) { this.y = y; } public void setRadius(int radius) { this.radius = radius; } @Override public void draw() { System.out.println("Circle: Draw() [Color : " + color +", x : " + x +", y :" + y +", radius :" + radius); } } //创建一个工厂,生成基于给定信息的实体类的对象。 import java.util.HashMap; public class ShapeFactory { private static final HashMap<String, Shape> circleMap = new HashMap<>(); public static Shape getCircle(String color) { Circle circle = (Circle)circleMap.get(color); if(circle == null) { circle = new Circle(color); circleMap.put(color, circle); System.out.println("Creating circle of color : " + color); } return circle; } } //使用该工厂,通过传递颜色信息来获取实体类的对象。 public class FlyweightPatternDemo { private static final String colors[] = { "Red", "Green", "Blue", "White", "Black" }; public static void main(String[] args) { for(int i=0; i < 20; ++i) { Circle circle = (Circle)ShapeFactory.getCircle(getRandomColor()); circle.setX(getRandomX()); circle.setY(getRandomY()); circle.setRadius(100); circle.draw(); } } private static String getRandomColor() { return colors[(int)(Math.random()*colors.length)]; } private static int getRandomX() { return (int)(Math.random()*100 ); } private static int getRandomY() { return (int)(Math.random()*100); } }
- 1.
享元模式应用场景
:系统有大量相似对象或需要连接池的场景- 2.
享元模式实际案例
:
- 1.
JAVA
中的String
常量池,如果有则返回,如果没有则创建一个字符串保存在字符串常量池中- 2.数据库连接的的连接池
2.外观模式(Facade Pattern)
- 1.
外观模式
隐藏了系统的复杂性并向客户端提供了一个可以访问系统的接口- 2.
外观模式
提供了客户端请求的简化方法和对现有系统类方法的委托调用- 3.优点:
外观模式
给各个子系统提供统一的入口,调用者使用起来更简单,且将各个子系统和调用者解耦,扩展性会更好- 4.缺点:如果设计不合理,增加新的子系统时可能需要修改外观类或调用者的源代码,违背
开闭原则
- 5.
关键
:客户端和复杂系统之间再封装一层处理,负责处理调用顺序、依赖关系等//创建一个接口 public interface Shape { void draw(); } //创建实现接口的实体类。 public class Rectangle implements Shape { @Override public void draw() { System.out.println("Rectangle::draw()"); } } public class Square implements Shape { @Override public void draw() { System.out.println("Square::draw()"); } } public class Circle implements Shape { @Override public void draw() { System.out.println("Circle::draw()"); } } //创建一个外观类 public class ShapeMaker { private Shape circle; private Shape rectangle; private Shape square; public ShapeMaker() { circle = new Circle(); rectangle = new Rectangle(); square = new Square(); } public void drawCircle(){ circle.draw(); } public void drawRectangle(){ rectangle.draw(); } public void drawSquare(){ square.draw(); } } //使用该外观类画出各种类型的形状。 public class FacadePatternDemo { public static void main(String[] args) { ShapeMaker shapeMaker = new ShapeMaker(); shapeMaker.drawCircle(); shapeMaker.drawRectangle(); shapeMaker.drawSquare(); } }
- 1.
外观模式应用场景
:当调用者需要调用多个子系统来完成自己的逻辑或需要为一个复杂的子系统提供一系列逻辑支持时- 2.
外观模式实际案例
:
- 1.
JAVA
中Class
中的forName(String name, boolean initialize ,ClassLoader loader)
方法调用了ClassLoader,System
接口
3.组合模式(Composite Pattern)
- 1.
组合模式
用于把一组相似的对象当作一个单一的对象,组合模式
依据树形结构
来组合对象,用来表示部分以及整体层次- 2.
组合模式
创建了一个包含自己对象组的类,该类提供了修改相同对象组的方式- 3.
组合模式
一般用来描述整体与部分的关系,将对象组织到树形结构中,顶层的节点被称为根节点,根节点下面可以包含树枝节点和叶子节点,树枝节点下面又可以包含树枝节点和叶子节点
- 4.优点
- 1.
组合模式
使代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,简化代码- 2.
组合模式
更容易在组合体内加入新的对象,不会因为加入了新的对象而更改源代码,满足开闭原则
- 5.缺点
- 1.设计较复杂,需要花更多时间理清类之间的层次关系
- 2.不容易限制容器中的构件
//创建 Employee 类,该类带有 Employee 对象的列表 import java.util.ArrayList; import java.util.List; public class Employee { private String name; private String dept; private int salary; private List<Employee> subordinates; //构造函数 public Employee(String name,String dept, int sal) { this.name = name; this.dept = dept; this.salary = sal; subordinates = new ArrayList<Employee>(); } public void add(Employee e) { subordinates.add(e); } public void remove(Employee e) { subordinates.remove(e); } public List<Employee> getSubordinates(){ return subordinates; } public String toString(){ return ("Employee :[ Name : "+ name +", dept : "+ dept + ", salary :" + salary+" ]"); } } //使用 Employee 类来创建和打印员工的层次结构。 public class CompositePatternDemo { public static void main(String[] args) { Employee CEO = new Employee("John","CEO", 30000); Employee headSales = new Employee("Robert","Head Sales", 20000); Employee headMarketing = new Employee("Michel","Head Marketing", 20000); Employee clerk1 = new Employee("Laura","Marketing", 10000); Employee clerk2 = new Employee("Bob","Marketing", 10000); Employee salesExecutive1 = new Employee("Richard","Sales", 10000); Employee salesExecutive2 = new Employee("Rob","Sales", 10000); CEO.add(headSales); CEO.add(headMarketing); headSales.add(salesExecutive1); headSales.add(salesExecutive2); headMarketing.add(clerk1); headMarketing.add(clerk2); //打印该组织的所有员工 System.out.println(CEO); for (Employee headEmployee : CEO.getSubordinates()) { System.out.println(headEmployee); for (Employee employee : headEmployee.getSubordinates()) { System.out.println(employee); } } } }
- 1.
组合模式应用场景
:当需要表示对象的部分-整体层次结构时可以使用该模式- 2.
组合模式实际案例
:Java
中AWT
中的容器
4.适配器模式(Adapter Pattern)
- 1.
适配器模式
是作为两个不兼容的接口之间的桥梁,结合了两个独立接口的功能- 2.将一个类型的接口转换目的接口,
适配器模式
使得原本由于接口不兼容而不能一起工作的类可以一起工作- 3.优点:极大的增强了程序的可扩展性,通过此模式可以随意扩展程序的功能,但却不需要修改接口
- 4.缺点: 过多地使用适配器会让系统非常零乱,不易整体进行把握
// 1.确定目标接口 public interface LogFactory { void debug(String tag,String message); } // 2.不兼容的三方库接口及实现 public interface NbLogger { void d(int priority, String message, Object ... obj); } //具体提供日志功能的实现类 public class NbLoggerImp implements NbLogger { @Override public void d(int priority, String message, Object... obj) { System.out.println(String.format("牛逼logger记录:%s",message)); } } // 3.适配器模式的核心,通过此类将三方库提供的接口转换为系统的目标接口 public class LogAdapter implements LogFactory { private NbLogger nbLogger; public LogAdapter(NbLogger nbLogger) { this.nbLogger = nbLogger; } @Override public void debug(String tag, String message) { Objects.requireNonNull(nbLogger); nbLogger.d(1, message); } } // 4.客户端使用 public class AdapterClient { public void recordLog() { LogFactory logFactory = new LogAdapter(new NbLoggerImp()); logFactory.debug("Test", "我将使用牛逼logger打印log"); } }
//为媒体播放器和更高级的媒体播放器创建接口。 public interface MediaPlayer { public void play(String audioType, String fileName); } public interface AdvancedMediaPlayer { public void playVlc(String fileName); public void playMp4(String fileName); } //创建实现了 AdvancedMediaPlayer 接口的实体类。 public class VlcPlayer implements AdvancedMediaPlayer{ @Override public void playVlc(String fileName) { System.out.println("Playing vlc file. Name: "+ fileName); } @Override public void playMp4(String fileName) { //什么也不做 } } public class Mp4Player implements AdvancedMediaPlayer{ @Override public void playVlc(String fileName) { //什么也不做 } @Override public void playMp4(String fileName) { System.out.println("Playing mp4 file. Name: "+ fileName); } } //创建实现了 MediaPlayer 接口的适配器类 public class MediaAdapter implements MediaPlayer { AdvancedMediaPlayer advancedMusicPlayer; public MediaAdapter(String audioType){ if(audioType.equalsIgnoreCase("vlc") ){ advancedMusicPlayer = new VlcPlayer(); } else if (audioType.equalsIgnoreCase("mp4")){ advancedMusicPlayer = new Mp4Player(); } } @Override public void play(String audioType, String fileName) { if(audioType.equalsIgnoreCase("vlc")){ advancedMusicPlayer.playVlc(fileName); }else if(audioType.equalsIgnoreCase("mp4")){ advancedMusicPlayer.playMp4(fileName); } } } //创建实现了 MediaPlayer 接口的实体类。 public class AudioPlayer implements MediaPlayer { MediaAdapter mediaAdapter; @Override public void play(String audioType, String fileName) { //播放 mp3 音乐文件的内置支持 if(audioType.equalsIgnoreCase("mp3")){ System.out.println("Playing mp3 file. Name: "+ fileName); } //mediaAdapter 提供了播放其他文件格式的支持 else if(audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")){ mediaAdapter = new MediaAdapter(audioType); mediaAdapter.play(audioType, fileName); } else{ System.out.println("Invalid media. "+ audioType + " format not supported"); } } } //使用 AudioPlayer 来播放不同类型的音频格式 public class AdapterPatternDemo { public static void main(String[] args) { AudioPlayer audioPlayer = new AudioPlayer(); audioPlayer.play("mp3", "beyond the horizon.mp3"); audioPlayer.play("mp4", "alone.mp4"); audioPlayer.play("vlc", "far far away.vlc"); audioPlayer.play("avi", "mind me.avi"); } }
- 1.
适配器模式应用场景
:当需要表示对象的部分-整体层次结构时可以使用该模式- 2.
适配器模式实际案例
:Java
中AWT
中的容器
5.代理模式(Proxy Pattern)
- 1.
代理模式
表示用一个类代表另一个类的功能,为其他对象提供一种代理以控制对这个对象的访问- 2.
代理模式
中创建具有现有对象的对象,以便向外界提供功能接口- 3.优点:职责清晰,扩展性高
- 4.缺点:由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢且实现代理模式需要额外的工作
- 5.代理模式种类
- 1.静态代理:代理类需要自己编写代码,代理类和真实类继承同一个父类或实现相同的接口,使用时只需要把真实类传递给代理类,代理类调用真实类色的方法即可
- 2.动态代理:代理类通过
Proxy
类中的反射动态生成,Java
的java.lang.reflect
包下提供了一个Proxy
类和一个InvocationHandler
接口,JDK
动态代理的实现主要依靠这两个对象实现- 3.
JDK
实现的代理中不管是静态代理还是动态代理,代理与被代理者都要实现接口,而CGLib
可以不需要接口,CGLib
动态代理是代理类去继承目标类,然后重写其中目标类的方法,具体可参考https://zhuanlan.zhihu.com/p/120343903
和https://zhuanlan.zhihu.com/p/115744594
- 6.注意
- 1.
代理模式
和适配器模式
区别:适配器模式主要改变对象的接口,而代理模式不能改变所代理类的接口- 2.
代理模式
和装饰器模式
区别:装饰器模式为了增强功能,而代理模式是为了加以控制//创建一个接口 public interface Image { void display(); } //创建实现接口的实体类 public class RealImage implements Image { private String fileName; public RealImage(String fileName){ this.fileName = fileName; loadFromDisk(fileName); } @Override public void display() { System.out.println("Displaying " + fileName); } private void loadFromDisk(String fileName){ System.out.println("Loading " + fileName); } } public class ProxyImage implements Image{ private RealImage realImage; private String fileName; public ProxyImage(String fileName){ this.fileName = fileName; } @Override public void display() { if(realImage == null){ realImage = new RealImage(fileName); } realImage.display(); } } //当被请求时,使用 ProxyImage 来获取 RealImage 类的对象。 public class ProxyPatternDemo { public static void main(String[] args) { Image image = new ProxyImage("test_10mb.jpg"); // 图像将从磁盘加载 image.display(); System.out.println(""); // 图像不需要从磁盘加载 image.display(); } }
1.静态代理
// 1.定义接口 package com.redis.proxy; public interface IRentBook { void rentBook(); } // 2.定义类实现接口 package com.redis.proxy.impl; import com.redis.proxy.IRentBook; public class RentBook implements IRentBook { @Override public void rentBook() { System.out.println("开始租书..."); } } // 3.定义代理类 package com.redis.proxy.impl; import com.redis.proxy.IRentBook;; public class IntermediaryProxy implements IRentBook{ private IRentBook rentBook; public IntermediaryProxy(IRentBook iRentBook){ rentBook = iRentBook; } @Override public void rentBook() { System.out.println("中介代理"); rentBook.rentBook(); } } // 4.测试 package com.redis.proxy; import com.redis.proxy.impl.IntermediaryProxy; import com.redis.proxy.impl.RentBook; public class Test { public static void main(String[] args){ // 1.定义租书 IRentBook rentBook = new RentBook(); // 2.定义中介 IRentBook intermediary = new IntermediaryProxy(rentBook); // 3.通过中介租书 intermediary.rentBook(); } }
2.强制代理
// 1.定义接口 package com.redis.proxy; public interface IRentBook { void rentBook(); IRentBook getProxy(); } // 2.定义类实现接口 package com.redis.proxy.impl; import com.redis.proxy.IRentBook; public class RentBook implements IRentBook { private IRentBook iRentBook = null; @Override public void rentBook() { if (isProxy()){ System.out.println("开始租书..."); }else { System.out.println("请找中介"); } } @Override public IRentBook getProxy() { iRentBook = new IntermediaryProxy(this); return iRentBook; } /** * 校验是否是代理访问 * @return */ private boolean isProxy(){ if(this.iRentBook == null){ return false; }else{ return true; } } } // 3.定义代理类 package com.redis.proxy.impl; import com.redis.proxy.IRentBook;; public class IntermediaryProxy implements IRentBook{ private IRentBook rentBook; public IntermediaryProxy(IRentBook iRentBook){ rentBook = iRentBook; } @Override public void rentBook() { System.out.println("中介代理"); rentBook.rentBook(); } @Override public IRentBook getProxy() { return this; } } // 4.测试 package com.redis.proxy; import com.redis.proxy.impl.RentBook; public class Test { public static void main(String[] args){ // 1.定义租书 IRentBook rentBook = new RentBook(); // 2.未通过代理租书 rentBook.rentBook(); // 3.获取代理 IRentBook proxy = rentBook.getProxy(); // 4.代理租书 proxy.rentBook(); } }
3.动态代理
// 1.定义接口 package com.redis.proxy; public interface IRentBook { void rentBook(); } // 2.定义类实现接口 package com.redis.proxy.impl; import com.redis.proxy.IRentBook; public class RentBook implements IRentBook { @Override public void rentBook() { System.out.println("开始租书..."); } } // 3.定义代理类 package com.redis.proxy.impl; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class IntermediaryProxy implements InvocationHandler { private Object obj; public IntermediaryProxy(Object object){ obj = object; } /** * 调用被代理的方法 * @param proxy * @param method * @param args * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = method.invoke(this.obj, args); return result; } } // 4.测试 package com.redis.proxy; import com.redis.proxy.impl.IntermediaryProxy; import com.redis.proxy.impl.LentBook; import com.redis.proxy.impl.RentBook; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; public class Test { public static void main(String[] args){ IRentBook rentHouse = new RentBook(); //定义一个租书handler InvocationHandler rHandler = new IntermediaryProxy(rentHouse); //获得类的class loader ClassLoader rcl = rentHouse.getClass().getClassLoader(); //Proxy通过反射cons.newInstance(new Object[]{h})动态产生一个代理者 //同时指定一个InvocationHandler的实现类,Proxy动态产生的代理会调用InvocationHandler 实现类,所以InvocationHandler是实际执行者 IRentBook rProxy = (IRentBook) Proxy.newProxyInstance(rcl, new Class[]{IRentBook.class}, rHandler); //代理类调用方法 rProxy.rentBook(); // 实现方式同租书 ILentBook lentBook = new LentBook(); InvocationHandler lHandler = new IntermediaryProxy(lentBook); ClassLoader lcl = lentBook.getClass().getClassLoader(); ILentBook lProxy = (ILentBook) Proxy.newProxyInstance(lcl, new Class[]{ILentBook.class}, lHandler); lProxy.lentBook(); } }
- 1.
代理模式应用场景
:控制对对象的访问或增强对象的功能- 2.
代理模式实际案例
:Spring
中的AOP
6.装饰器模式(Decorator Pattern)
- 1.
装饰器模式
是在不改变现有对象结构的情况下动态地给该对象增加一些额外功能- 2.
装饰器模式
创建了一个装饰类包装原有的类,并在保持类方法签名完整性的前提下提供了额外的功能- 3.装饰类和被装饰类可以独立发展,不会相互耦合,
装饰器模式
比继承的一个更加灵活,装饰模式可以动态扩展一个实现类的功能- 4.优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能
- 5.缺点:装饰器模式会增加许多子类,过度使用会增加程序得复杂性
- 6.注意:装饰器可代替继承
//创建一个接口 public interface Shape { void draw(); } //创建实现接口的实体类 public class Rectangle implements Shape { @Override public void draw() { System.out.println("Shape: Rectangle"); } } public class Circle implements Shape { @Override public void draw() { System.out.println("Shape: Circle"); } } //创建实现了 Shape 接口的抽象装饰类。 public abstract class ShapeDecorator implements Shape { protected Shape decoratedShape; public ShapeDecorator(Shape decoratedShape){ this.decoratedShape = decoratedShape; } public void draw(){ decoratedShape.draw(); } } //创建扩展了 ShapeDecorator 类的实体装饰类。 public class RedShapeDecorator extends ShapeDecorator { public RedShapeDecorator(Shape decoratedShape) { super(decoratedShape); } @Override public void draw() { decoratedShape.draw(); setRedBorder(decoratedShape); } private void setRedBorder(Shape decoratedShape){ System.out.println("Border Color: Red"); } } //使用 RedShapeDecorator 来装饰 Shape 对象。 public class DecoratorPatternDemo { public static void main(String[] args) { Shape circle = new Circle(); ShapeDecorator redCircle = new RedShapeDecorator(new Circle()); ShapeDecorator redRectangle = new RedShapeDecorator(new Rectangle()); //Shape redCircle = new RedShapeDecorator(new Circle()); //Shape redRectangle = new RedShapeDecorator(new Rectangle()); System.out.println("Circle with normal border"); circle.draw(); System.out.println("\nCircle of red border"); redCircle.draw(); System.out.println("\nRectangle of red border"); redRectangle.draw(); } }
- 1.
装饰器模式应用场景
:当需要给一个现有类添加附加功能,而又不能采用生成子类的方法进行扩充时- 2.
装饰器模式实际案例
:InputStream
的子类FilterInputStream
7.桥接模式(Composite Pattern)
- 1.
桥接模式
指把抽象化与实现化解耦,使得二者可以独立变化- 2.
桥接模式
涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类,这两种类型的类可被结构化改变而互不影响- 3.目的:将抽象部分与实现部分分离,使其都可以独立的变化,具体可参考
https://blog.csdn.net/weixin_39296283/article/details/104953668
//创建桥接实现接口。 public interface DrawAPI { public void drawCircle(int radius, int x, int y); } //创建实现了 DrawAPI 接口的实体桥接实现类。 public class RedCircle implements DrawAPI { @Override public void drawCircle(int radius, int x, int y) { System.out.println("Drawing Circle[ color: red, radius: " + radius +", x: " +x+", "+ y +"]"); } } public class GreenCircle implements DrawAPI { @Override public void drawCircle(int radius, int x, int y) { System.out.println("Drawing Circle[ color: green, radius: " + radius +", x: " +x+", "+ y +"]"); } } //使用 DrawAPI 接口创建抽象类 Shape。 public abstract class Shape { protected DrawAPI drawAPI; protected Shape(DrawAPI drawAPI){ this.drawAPI = drawAPI; } public abstract void draw(); } //创建实现了 Shape 抽象类的实体类。 public class Circle extends Shape { private int x, y, radius; public Circle(int x, int y, int radius, DrawAPI drawAPI) { super(drawAPI); this.x = x; this.y = y; this.radius = radius; } public void draw() { drawAPI.drawCircle(radius,x,y); } } //使用 Shape 和 DrawAPI 类画出不同颜色的圆 public class BridgePatternDemo { public static void main(String[] args) { Shape redCircle = new Circle(100,100, 10, new RedCircle()); Shape greenCircle = new Circle(100,100, 10, new GreenCircle()); redCircle.draw(); greenCircle.draw(); } }
- 1.
桥接模式应用场景
:一个类存在两个独立变化的维度,且这两个维度都需要进行扩展- 2.
桥接模式实际案例
:JDBC
连接中的驱动类Driver
为桥接对象
3.行为型模式(Behavioral Pattern)
- 1.
行为型模式
不仅仅关注类和对象的结构,更关注于对象的行为问题,是对在不同的对象之间划分责任和算法的抽象化(11
种)
- 1.
状态模式
- 2.
责任链模式
- 3.
中介者模式
- 4.
模板方法模式
- 5.
访问者模式
- 6.
解释器模式
- 7.
备忘录模式
- 8.
观察者模式
- 9.
策略模式
- 10.
命令模式
- 11.
迭代器模式
- 2.记忆口诀:行状责中模访解备观策命迭(形状折中模仿,戒备观测鸣笛)
1.状态模式(State Pattern)
- 1.
状态模式
指类的行为是基于它的状态改变的,状态模式中需要创建表示各种状态的对象和一个行为随着状态对象改变而改变的context
对象- 2.对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变相关行为
- 3.优点:增强了程序的可扩展性和封装性,每个状态的操作都被封装到了一个状态类中
- 4.缺点:类的数量变多
//创建一个接口 public interface State { public void doAction(Context context); } //创建实现接口的实体类。 public class StartState implements State { public void doAction(Context context) { System.out.println("Player is in start state"); context.setState(this); } public String toString(){ return "Start State"; } } public class StopState implements State { public void doAction(Context context) { System.out.println("Player is in stop state"); context.setState(this); } public String toString(){ return "Stop State"; } } //创建 Context 类。 public class Context { private State state; public Context(){ state = null; } public void setState(State state){ this.state = state; } public State getState(){ return state; } } //使用 Context 来查看当状态 State 改变时的行为变化 public class StatePatternDemo { public static void main(String[] args) { Context context = new Context(); StartState startState = new StartState(); startState.doAction(context); System.out.println(context.getState().toString()); StopState stopState = new StopState(); stopState.doAction(context); System.out.println(context.getState().toString()); } }
- 1.
状态模式应用场景
:代码中包含大量与对象状态有关的条件语句,不便于扩展和修改- 2.
状态模式实际案例
:Spring
状态机
2.责任链模式(Chain of Responsibility Pattern)
- 1.
责任链模式
为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链,当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止- 2.
责任链模式
中通常每个接收者都包含对另一个接收者的引用,如果一个对象不能处理该请求则会把相同的请求传给下一个接收者,依此类推- 3.优点
- 1.降低了对象之间的耦合度,该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息
- 2.增强了系统的可扩展性,可以根据需要增加新的请求处理类,满足开闭原则
- 3.增强了给对象指派职责的灵活性,当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任
- 4.责任链简化了对象之间的连接,每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用
- 5.责任分担,每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则
- 4.缺点
- 1.不能保证每个请求一定被处理,由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理
- 2.职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错
- 5.关键
- 1.拦截器都实现统一接口或继承统一抽象类
- 2.聚合本身并有指向下一个处理对象的指针,如果没达到处理条件则向下传递
//创建抽象的记录器类 public abstract class AbstractLogger { public static int INFO = 1; public static int DEBUG = 2; public static int ERROR = 3; protected int level; //责任链中的下一个元素 protected AbstractLogger nextLogger; public void setNextLogger(AbstractLogger nextLogger){ this.nextLogger = nextLogger; } public void logMessage(int level, String message){ if(this.level <= level){ write(message); } if(nextLogger !=null){ nextLogger.logMessage(level, message); } } abstract protected void write(String message); } //创建扩展了该记录器类的实体类 public class ConsoleLogger extends AbstractLogger { public ConsoleLogger(int level){ this.level = level; } @Override protected void write(String message) { System.out.println("Standard Console::Logger: " + message); } } public class ErrorLogger extends AbstractLogger { public ErrorLogger(int level){ this.level = level; } @Override protected void write(String message) { System.out.println("Error Console::Logger: " + message); } } public class FileLogger extends AbstractLogger { public FileLogger(int level){ this.level = level; } @Override protected void write(String message) { System.out.println("File::Logger: " + message); } } //创建不同类型的记录器。赋予它们不同的错误级别,并在每个记录器中设置下一个记录器。每个记录器中的下一个记录器代表的是链的一部分。 public class ChainPatternDemo { private static AbstractLogger getChainOfLoggers(){ AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR); AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG); AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO); errorLogger.setNextLogger(fileLogger); fileLogger.setNextLogger(consoleLogger); return errorLogger; } public static void main(String[] args) { AbstractLogger loggerChain = getChainOfLoggers(); loggerChain.logMessage(AbstractLogger.INFO, "This is an information."); loggerChain.logMessage(AbstractLogger.DEBUG, "This is a debug level information."); loggerChain.logMessage(AbstractLogger.ERROR, "This is an error information."); } }
- 1.
责任链模式应用场景
:有多个对象可以处理一个请求,哪个对象处理该请求由运行时刻自动确定- 2.
责任链模式实际案例
:Spring Security
3.中介者模式(Mediator Pattern)
- 1.
中介者模式
用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互- 2.
中介者模式
提供一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护- 3.优点: 降低了类的复杂度,将一对多转化成了一对一,且各个类之间的解耦
- 4.缺点:中介者会逐渐庞大,变得复杂难以维护
//创建中介类 import java.util.Date; public class ChatRoom { public static void showMessage(User user, String message){ System.out.println(new Date().toString() + " [" + user.getName() +"] : " + message); } } //创建 user 类 public class User { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public User(String name){ this.name = name; } public void sendMessage(String message){ ChatRoom.showMessage(this,message); } } //使用 User 对象来显示他们之间的通信 public class MediatorPatternDemo { public static void main(String[] args) { User robert = new User("Robert"); User john = new User("John"); robert.sendMessage("Hi! John!"); john.sendMessage("Hello! Robert!"); } }
- 1.
中介者模式应用场景
:系统中对象之间存在比较复杂的引用关系,导致它们之间的依赖关系结构混乱而且难以复用该对象- 2.
中介者模式实际案例
:Timer
类中的sched
方法
4.模板模式(Template Pattern)
- 1.
模板模式
指一个抽象类公开定义了执行它的方法的方式/模板,其子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行- 2.
模板模式
定义一个操作中的算法的骨架而将具体步骤延迟到子类中,模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤- 3.优点:封装不变部分,扩展可变部分,提取公共代码,便于维护,其行为由父类控制,子类实现
- 4.缺点:每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大
- 5.注意:为防止恶意操作一般模板方法都加上
final
关键词,这样就不会被重写//创建一个抽象类,它的模板方法被设置为 final。 public abstract class Game { abstract void initialize(); abstract void startPlay(); abstract void endPlay(); //模板 public final void play(){ //初始化游戏 initialize(); //开始游戏 startPlay(); //结束游戏 endPlay(); } } //创建扩展了上述类的实体类。 public class Cricket extends Game { @Override void endPlay() { System.out.println("Cricket Game Finished!"); } @Override void initialize() { System.out.println("Cricket Game Initialized! Start playing."); } @Override void startPlay() { System.out.println("Cricket Game Started. Enjoy the game!"); } } public class Football extends Game { @Override void endPlay() { System.out.println("Football Game Finished!"); } @Override void initialize() { System.out.println("Football Game Initialized! Start playing."); } @Override void startPlay() { System.out.println("Football Game Started. Enjoy the game!"); } } //使用 Game 的模板方法 play() 来演示游戏的定义方式。 public class TemplatePatternDemo { public static void main(String[] args) { Game game = new Cricket(); game.play(); System.out.println(); game = new Football(); game.play(); } }
- 1.
模板模式应用场景
:有多个子类共有的方法,且逻辑相同- 2.
模板者模式实际案例
:spring
中对Hibernate
的支持,将一些已经定好的方法封装起来
5.访问者模式(Visitor Pattern)
- 1.
访问者模式
使用了一个访问者类改变元素类的执行算法,通过这种方式,元素的执行算法可以随着访问者改变而改变- 2.目的:不改变要访问对象的前提下,对访问对象的操作做拓展,将数据结构与数据操作分离
//定义一个表示元素的接口 public interface ComputerPart { public void accept(ComputerPartVisitor computerPartVisitor); } //创建扩展了上述类的实体类。 public class Keyboard implements ComputerPart { @Override public void accept(ComputerPartVisitor computerPartVisitor) { computerPartVisitor.visit(this); } } public class Monitor implements ComputerPart { @Override public void accept(ComputerPartVisitor computerPartVisitor) { computerPartVisitor.visit(this); } } public class Mouse implements ComputerPart { @Override public void accept(ComputerPartVisitor computerPartVisitor) { computerPartVisitor.visit(this); } } public class Computer implements ComputerPart { ComputerPart[] parts; public Computer(){ parts = new ComputerPart[] {new Mouse(), new Keyboard(), new > Monitor()}; } @Override public void accept(ComputerPartVisitor computerPartVisitor) { for (int i = 0; i < parts.length; i++) { parts[i].accept(computerPartVisitor); } computerPartVisitor.visit(this); } } //定义一个表示访问者的接口 public interface ComputerPartVisitor { public void visit(Computer computer); public void visit(Mouse mouse); public void visit(Keyboard keyboard); public void visit(Monitor monitor); } //创建实现了上述类的实体访问者。 public class ComputerPartDisplayVisitor implements ComputerPartVisitor { @Override public void visit(Computer computer) { System.out.println("Displaying Computer."); } @Override public void visit(Mouse mouse) { System.out.println("Displaying Mouse."); } @Override public void visit(Keyboard keyboard) { System.out.println("Displaying Keyboard."); } @Override public void visit(Monitor monitor) { System.out.println("Displaying Monitor."); } } //使用 ComputerPartDisplayVisitor 来显示 Computer 的组成部分。 public class VisitorPatternDemo { public static void main(String[] args) { ComputerPart computer = new Computer(); computer.accept(new ComputerPartDisplayVisitor()); } }
- 1.
访问者模式应用场景
:类中包含各种类型的元素,不会经常增删不同类型的元素,而需要经常给这些元素添加新的操作- 2.
访问者模式实际案例
:FileVisitor
类
6.解释器模式(Interpreter Pattern)
- 1.
解释器模式
提供了评估语言的语法或表达式的方式,给定一个语言定义其文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子- 2.
解释器模式
实现了一个表达式接口,该接口解释一个特定的上下文- 3.优点: 可扩展性比较好,灵活,增加了新的解释表达式的方式, 易于实现简单文法
- 4.缺点: 解释器模式会引起类膨胀,对于复杂的文法比较难维护
//创建一个表达式接口。 public interface Expression { public boolean interpret(String context); } //创建实现了上述接口的实体类。 public class TerminalExpression implements Expression { private String data; public TerminalExpression(String data){ this.data = data; } @Override public boolean interpret(String context) { if(context.contains(data)){ return true; } return false; } } public class OrExpression implements Expression { private Expression expr1 = null; private Expression expr2 = null; public OrExpression(Expression expr1, Expression expr2) { this.expr1 = expr1; this.expr2 = expr2; } @Override public boolean interpret(String context) { return expr1.interpret(context) || expr2.interpret(context); } } public class AndExpression implements Expression { private Expression expr1 = null; private Expression expr2 = null; public AndExpression(Expression expr1, Expression expr2) { this.expr1 = expr1; this.expr2 = expr2; } @Override public boolean interpret(String context) { return expr1.interpret(context) && expr2.interpret(context); } } //InterpreterPatternDemo 使用 Expression 类来创建规则,并解析它们。 public class InterpreterPatternDemo { //规则:Robert 和 John 是男性 public static Expression getMaleExpression(){ Expression robert = new TerminalExpression("Robert"); Expression john = new TerminalExpression("John"); return new OrExpression(robert, john); } //规则:Julie 是一个已婚的女性 public static Expression getMarriedWomanExpression(){ Expression julie = new TerminalExpression("Julie"); Expression married = new TerminalExpression("Married"); return new AndExpression(julie, married); } public static void main(String[] args) { Expression isMale = getMaleExpression(); Expression isMarriedWoman = getMarriedWomanExpression(); System.out.println("John is male? " + isMale.interpret("John")); System.out.println("Julie is a married women? " + isMarriedWoman.interpret("Married Julie")); } }
- 1.
解释器模式应用场景
:一个简单语法需要解释的场景或一些重复出现的问题可以用一种简单的语言来进行表达- 2.
解释器模式实际案例
:SQL
解析、符号处理引擎
7.备忘录模式(Memento Pattern)
- 1.
备忘录模式
指保存一个对象的某个状态,以便在适当的时候恢复对象- 2.
备忘录模式
就是在不破坏封装的前提下捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态- 3.优点: 提供了一种可以恢复状态的机制,可以比较方便地回到某个历史的状态
- 4.缺点:消耗资源,如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存
//创建 Memento 类 public class Memento { private String state; public Memento(String state){ this.state = state; } public String getState(){ return state; } } //创建 Originator 类。 public class Originator { private String state; public void setState(String state){ this.state = state; } public String getState(){ return state; } public Memento saveStateToMemento(){ return new Memento(state); } public void getStateFromMemento(Memento Memento){ state = Memento.getState(); } } //创建 CareTaker 类。 import java.util.ArrayList; import java.util.List; public class CareTaker { private List<Memento> mementoList = new ArrayList<Memento>(); public void add(Memento state){ mementoList.add(state); } public Memento get(int index){ return mementoList.get(index); } } //使用 CareTaker 和 Originator 对象 public class MementoPatternDemo { public static void main(String[] args) { Originator originator = new Originator(); CareTaker careTaker = new CareTaker(); originator.setState("State #1"); originator.setState("State #2"); careTaker.add(originator.saveStateToMemento()); originator.setState("State #3"); careTaker.add(originator.saveStateToMemento()); originator.setState("State #4"); System.out.println("Current State: " + originator.getState()); originator.getStateFromMemento(careTaker.get(0)); System.out.println("First saved State: " + originator.getState()); originator.getStateFromMemento(careTaker.get(1)); System.out.println("Second saved State: " + originator.getState()); } }
- 1.
备忘录模式应用场景
:需要保存/恢复数据的相关状态场景或提供一个可回滚的操作- 2.
备忘录模式实际案例
:数据库的事务回滚
8.观察者模式(Observer Pattern)
- 1.
观察者模式
指当一个对象被修改时,则会自动通知依赖它的对象- 2.
观察者模式
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新(广播通知)- 3.优点: 观察者和被观察者是抽象耦合的,耦合度低
- 4.缺点: 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化
- 5.注意:避免循环引用,一般采用异步方式
//创建 Subject 类 import java.util.ArrayList; import java.util.List; public class Subject { private List<Observer> observers = new ArrayList<Observer>(); private int state; public int getState() { return state; } public void setState(int state) { this.state = state; notifyAllObservers(); } public void attach(Observer observer){ observers.add(observer); } public void notifyAllObservers(){ for (Observer observer : observers) { observer.update(); } } } //创建 Observer 类 public abstract class Observer { protected Subject subject; public abstract void update(); } //创建实体观察者类 public class BinaryObserver extends Observer{ public BinaryObserver(Subject subject){ this.subject = subject; this.subject.attach(this); } @Override public void update() { System.out.println( "Binary String: " + Integer.toBinaryString( subject.getState() ) ); } } public class OctalObserver extends Observer{ public OctalObserver(Subject subject){ this.subject = subject; this.subject.attach(this); } @Override public void update() { System.out.println( "Octal String: " + Integer.toOctalString( subject.getState() ) ); } } public class HexaObserver extends Observer{ public HexaObserver(Subject subject){ this.subject = subject; this.subject.attach(this); } @Override public void update() { System.out.println( "Hex String: " + Integer.toHexString( subject.getState() ).toUpperCase() ); } } //使用 Subject 和实体观察者对象。 public class ObserverPatternDemo { public static void main(String[] args) { Subject subject = new Subject(); new HexaObserver(subject); new OctalObserver(subject); new BinaryObserver(subject); System.out.println("First state change: 15"); subject.setState(15); System.out.println("Second state change: 10"); subject.setState(10); } }
- 1.
观察者模式应用场景
:一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度- 2.
观察者模式实际案例
:
- 1.
JDK
中的观察者接口是java.util.Observer
,观察者类是java.util.Observable
- 2.
Spring
框架中的观察者类是org.springframework.context.ApplicationListener
类
9.策略模式(Strategy Pattern)
- 1.
策略模式
定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的用户- 2.
策略模式
使一个类的行为或其算法可以在运行时更改,创建表示各种策略的对象和一个行为随着策略对象改变而改变的context
对象策略对象改变context
对象的执行算法- 3.优点: 算法可以自由切换,避免使用多重条件判断,扩展性好
- 4.缺点: 策略类会增多,所有策略类都需要对外暴露
- 5.关键:策略类实现同一个接口
//创建一个接口 public interface Strategy { public int doOperation(int num1, int num2); } //创建实现接口的实体类 public class OperationAdd implements Strategy{ @Override public int doOperation(int num1, int num2) { return num1 + num2; } } public class OperationSubtract implements Strategy{ @Override public int doOperation(int num1, int num2) { return num1 - num2; } } public class OperationMultiply implements Strategy{ @Override public int doOperation(int num1, int num2) { return num1 * num2; } } //创建 Context 类 public class Context { private Strategy strategy; public Context(Strategy strategy){ this.strategy = strategy; } public int executeStrategy(int num1, int num2){ return strategy.doOperation(num1, num2); } } //使用 Context 来查看当它改变策略 Strategy 时的行为变化 public class StrategyPatternDemo { public static void main(String[] args) { Context context = new Context(new OperationAdd()); System.out.println("10 + 5 = " + context.executeStrategy(10, 5)); context = new Context(new OperationSubtract()); System.out.println("10 - 5 = " + context.executeStrategy(10, 5)); context = new Context(new OperationMultiply()); System.out.println("10 * 5 = " + context.executeStrategy(10, 5)); } }
- 1.
策略模式应用场景
:一个系统需要动态地在几种算法中选择一种- 2.
策略模式实际案例
:ThreadPoolExecutor
中的四种拒绝策略, 比较器Comparator
10.命令模式(Command Pattern)
- 1.
命令模式
指将请求以命令的形式包裹在对象中,并传给调用对象,调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令- 2.优点:降低了系统耦合度,新的命令可以很容易添加到系统中去
- 3.缺点:使用命令模式可能会导致某些系统有过多的具体命令类
//创建一个命令接口 public interface Order { void execute(); } //创建一个请求类 public class Stock { private String name = "ABC"; private int quantity = 10; public void buy(){ System.out.println("Stock [ Name: "+name+", Quantity: " + quantity +" ] bought"); } public void sell(){ System.out.println("Stock [ Name: "+name+", Quantity: " + quantity +" ] sold"); } } //创建实现了 Order 接口的实体类 public class BuyStock implements Order { private Stock abcStock; public BuyStock(Stock abcStock){ this.abcStock = abcStock; } public void execute() { abcStock.buy(); } } public class SellStock implements Order { private Stock abcStock; public SellStock(Stock abcStock){ this.abcStock = abcStock; } public void execute() { abcStock.sell(); } } //创建命令调用类 import java.util.ArrayList; import java.util.List; public class Broker { private List<Order> orderList = new ArrayList<Order>(); public void takeOrder(Order order){ orderList.add(order); } public void placeOrders(){ for (Order order : orderList) { order.execute(); } orderList.clear(); } } //使用 Broker 类来接受并执行命令 public class CommandPatternDemo { public static void main(String[] args) { Stock abcStock = new Stock(); BuyStock buyStockOrder = new BuyStock(abcStock); SellStock sellStockOrder = new SellStock(abcStock); Broker broker = new Broker(); broker.takeOrder(buyStockOrder); broker.takeOrder(sellStockOrder); broker.placeOrders(); } }
- 1.
命令模式应用场景
:需要对行为进行记录、撤销或重做、事务- 2.
命令模式实际案例
:struts 1
中的action
核心控制器ActionServlet
11.迭代器模式(Iterator Pattern)
- 1.
迭代器模式
提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示- 2.
迭代器模式
用于顺序访问集合对象的元素,不需要知道集合对象的底层表示- 3.优点: 支持以不同的方式遍历一个聚合对象,增加新的聚合类和迭代器类都很方便,无须修改原有代码
- 4.缺点:一定程度上增加了系统的复杂性
- 5.注意:迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据
//创建接口 public interface Iterator { public boolean hasNext(); public Object next(); } public interface Container { public Iterator getIterator(); } //创建实现了 Container 接口的实体类。该类有实现了 Iterator 接口的内部类 NameIterator。 public class NameRepository implements Container { public String[] names = {"Robert" , "John" ,"Julie" , "Lora"}; @Override public Iterator getIterator() { return new NameIterator(); } private class NameIterator implements Iterator { int index; @Override public boolean hasNext() { if(index < names.length){ return true; } return false; } @Override public Object next() { if(this.hasNext()){ return names[index++]; } return null; } } } //使用 NameRepository 来获取迭代器,并打印名字。 public class IteratorPatternDemo { public static void main(String[] args) { NameRepository namesRepository = new NameRepository(); for(Iterator iter = namesRepository.getIterator(); iter.hasNext();){ String name = (String)iter.next(); System.out.println("Name : " + name); } } }
- 1.
迭代器模式应用场景
:需要为聚合对象提供多种遍历方式,为遍历不同的聚合结构提供一个统一的接口- 2.
迭代器模式实际案例
:JAVA
中的iterator