设计模式
软件设计原则
开闭原则
在程序需要进行扩展的时候,不能够去修改原有的代码,实现一个热插拔的效果。接口和抽象类就是开闭原则最好的体现。
里氏代换原则
子类可以扩展父类的功能,但是不能改变父类原有的功能。通俗来说就是子类重写了父类的方法,在添加新的功能的同时,尽量不要重写父类的方法。
如果通过重写父类的方法完成新功能,这样的方法看起来简单,但是会使继承体系可复用性变得比较差。
依赖倒转原则
依赖倒转原则按照业务逻辑来说,比如说我们开发用到的三层架构,我们会调用Service接口,Service接口让他的子类再进行业务方法的实现,可以实现热插拔。
接口隔离原则
假设一个类A,有两个方法:方法1和方法2。这个时候B类想要A类中的方法1,但是不想要方法2。
迪米特法则
如果两个软件实体无需直接通信,那么就不应当发生直接的互相调用,可以通过第三方转发该调用,目的是为了降低类之间的耦合度,提高模块的相对独立性。
在生活中,我们租房不是直接找房东,会有房屋中介,房屋中介和房东进行沟通。
合成复用原则
采用组合或者聚合等关联实现复用
有采用类的继承实现复用,但是会破坏了类的封装性,子类与父类的耦合度降低,限制了复用的灵活性
创建型设计模式
单例设计模式
单例设计模式是这个对象只会被创建一次。实现是通过私有构造方法,然后在类中创建一个对象,提供一个静态方法,返回这个对象,给外界使用。
单例设计模式-饿汉式
饿汉式是一种在类加载的时候,就已经把对象创建出来,调用者使用。
单例设计模式-饿汉式-静态成员变量
public class Singleton {
// 单例设计模式-饿汉式-静态成员变量实现
private Singleton(){}
private static Singleton singleton =new Singleton();
public static Singleton getInstance(){
return singleton;
}
}
单例设计模式-饿汉式-静态代码块
public class SingletonDemo2 {
// 单例设计模式-饿汉式-静态代码块
private SingletonDemo2(){}
private static SingletonDemo2 instance;
static {
instance=new SingletonDemo2();
}
public static SingletonDemo2 getInstance(){
return instance;
}
}
单例设计模式-懒汉式
懒汉式是调用实例方法的时候才会被创建对象
单例设计模式-懒汉式-双重加锁方式
我使用是双重加锁的懒汉式
public class SingletonLazy {
// 单例设计模式-双重检查锁模式
private SingletonLazy() {
}
// 加上volatile 关键字 防止JVM指令优化,指令重排 导致空指针
private static volatile SingletonLazy instance;
public static SingletonLazy getInstance() {
if (instance==null){
// 加锁,防止在多线程情况下出现线程安全问题
synchronized (SingletonLazy.class){
if(instance==null){
instance=new SingletonLazy();
return instance;
}
}
}
return instance;
}
}
这里我把锁注释掉了,volatile去掉,用测试类验证线程安全问题
测试类,开启了100000个线程
发现有false,说明不加锁会存在线程安全问题
然后再把锁加上,不加上volatile关键字
开启200000个线程进行测试,发现不加上volatile关键字,会导致指令重排,会有空指针的问题
单例设计模式-懒汉式-静态内部类
静态内部类单例模式中实例由内部类创建,由于 JVM 在加载外部类的过程中, 是不会加载静态内部类的, 只有内部类的属性/方法被调用时才会被加载, 并初始化其静态属性
public class SingletonStatic {
// 单例模式-饿汉式-静态内部类
private SingletonStatic() {
}
private static class SingletonHolder {
private static final SingletonStatic INSTANCE = new SingletonStatic();
}
public static SingletonStatic getInstance() {
return SingletonHolder.INSTANCE;
}
}
单例设计模式-饿汉式-枚举类
枚举类实现单例模式是极力推荐的单例实现模式,因为枚举类型是线程安全的,并且只会装载一次,设计者充分的利用了枚举的这个特性来实现单例模式,枚举的写法非常简单,而且枚举类型是所用单例实现中唯一一种不会被破坏的单例实现模式。
public enum SingletonEnum {
// 单例设计模式-懒汉式-枚举类(线程安全,而且不会被反射和序列化破坏)
INSTANCE
}
序列化破坏单例设计模式
序列化与反序列化会破坏单例模式,如下图两次读取到的不是同一个对象
解决方案:在SingletonStatic类中上以下方法
反射破坏单例模式
反射会破坏单例模式,因为反射可以暴力获取构造方法并且创建新的对象
解决方案:我们可以在SingletonStatic类中的私有构造方法中进行判断,具体如下代码
public class SingletonStatic {
private static boolean flag=false;// 默认为false
// 单例模式-饿汉式-静态内部类
private SingletonStatic() {
// 如果为true就说明 已经创建过一次了,就抛出异常不让他创建了
// 考虑到多线程情况下,flag变量是共享资源,可能造成线程安全问题,于是进行加锁
synchronized (SingletonStatic.class) {
if (flag) {
throw new RuntimeException("It's not First Creat");
}
flag = true;
}
}
private static class SingletonHolder {
private static final SingletonStatic INSTANCE = new SingletonStatic();
}
public static SingletonStatic getInstance() {
return SingletonHolder.INSTANCE;
}
// 在Singleton类中添加`readResolve()`方法,在反序列化时被反射调用,如果定义了这个方法,就返回这个方法的值,如果没有定义,则返回新new出来的对象。
private Object readResolve() {
return SingletonHolder.INSTANCE;
}
}
jdk源码中RunTime类就使用的是单例模式。(使用的是饿汉式中的静态成员变量)
工厂模式
简单工厂模式(不属于GOF23种设计模式之一)
简单工厂不是一种设计模式,反而比较像是一种编程习惯。
- 优点:实现了简单的解耦合
- 缺点:违背了开闭原则
具体实现代码:
抽象咖啡类:
/**
* @Autor: wl
* @Date: 2021/8/14 21:02
* @Version 1.0
*/
public abstract class Coffee {
public abstract String getName();
// 加糖
public void addSugar() {
System.out.println("加糖");
}
// 加奶
public void addMilk() {
System.out.println("加奶");
}
}
咖啡商店类
/**
* @Autor: wl
* @Date: 2021/8/14 21:08
* @Version 1.0
*/
public class CoffeeStore{
// 点咖啡方法
public Coffee orderCoffer(String type){
// 调用工厂模式 来创建咖啡
Coffee coffee = SimpleCoffeeFactory.createCoffee(type);
coffee.addMilk();
coffee.addSugar();
return coffee;
}
}
具体的实现类-拿铁咖啡
/**
* @Autor: wl
* @Date: 2021/8/14 21:05
* @Version 1.0
*/
public class LatteCoffee extends Coffee{
@Override
public String getName() {
return "拿铁咖啡";
}
}
简单咖啡工厂
/**
* @Autor: wl
* @Date: 2021/8/16 9:01
* @Version 1.0
*/
public class SimpleCoffeeFactory {
// 简单工厂模式
public static Coffee createCoffee(String type){
Coffee coffee=null;
if ("拿铁咖啡".equals(type)){
coffee=new LatteCoffee();
}else if ("美式咖啡".equals(type)){
coffee=new AmericanCoffee();
}else {
throw new RuntimeException("Error");
}
return coffee;
}
}
美式咖啡
/**
* @Autor: wl
* @Date: 2021/8/14 21:07
* @Version 1.0
*/
public class AmericanCoffee extends Coffee {
@Override
public String getName() {
return "美式咖啡";
}
}
测试类
/**
* @Autor: wl
* @Date: 2021/8/14 21:11
* @Version 1.0
*/
public class TestCoffee {
public static void main(String[] args) {
Coffee coffee = new CoffeeStore().orderCoffer("拿铁咖啡");
System.out.println(coffee.getName());
}
}
工厂方法模式(适用于同一类产品)
工厂方法模式是对简单工厂模式的改进,它定义了一个工厂接口,让产品工厂实现他的方法,创建对应的产品,实现解耦合。
具体实现代码:
抽象咖啡工厂
/**
* @Autor: wl
* @Date: 2021/8/16 15:17
* @Version 1.0
*/
public interface CoffeeFactory {
// 咖啡工厂
Coffee createCoffee();
}
抽象咖啡工程抽象类
/**
* @Autor: wl
* @Date: 2021/8/16 15:20
* @Version 1.0
*/
public class AmericanCoffeeFactory implements CoffeeFactory{
// 生产美式咖啡
@Override
public Coffee createCoffee() {
return new AmericanCoffee();
}
}
/**
* @Autor: wl
* @Date: 2021/8/16 15:21
* @Version 1.0
*/
public class LatteCoffeeFactory implements CoffeeFactory {
// 生产拿铁咖啡
@Override
public Coffee createCoffee() {
return new LatteCoffee();
}
}
咖啡实体类
/**
* @Autor: wl
* @Date: 2021/8/14 21:02
* @Version 1.0
*/
public abstract class Coffee {
public abstract String getName();
// 加糖
public void addSugar() {
System.out.println("加糖");
}
// 加奶
public void addMilk() {
System.out.println("加奶");
}
}
抽象咖啡类
/**
* @Autor: wl
* @Date: 2021/8/14 21:02
* @Version 1.0
*/
public abstract class Coffee {
public abstract String getName();
// 加糖
public void addSugar() {
System.out.println("加糖");
}
// 加奶
public void addMilk() {
System.out.println("加奶");
}
}
咖啡实现类
/**
* @Autor: wl
* @Date: 2021/8/14 21:07
* @Version 1.0
*/
public class AmericanCoffee extends Coffee {
@Override
public String getName() {
return "美式咖啡";
}
}
/**
* @Autor: wl
* @Date: 2021/8/14 21:05
* @Version 1.0
*/
public class LatteCoffee extends Coffee {
@Override
public String getName() {
return "拿铁咖啡";
}
}
咖啡商店
/**
* @Autor: wl
* @Date: 2021/8/14 21:08
* @Version 1.0
*/
public class CoffeeStore{
private CoffeeFactory factory;
public void setFactory(CoffeeFactory factory){
this.factory=factory;
}
// 点咖啡方法
public Coffee orderCoffer(){
// 调用工厂模式 来创建咖啡
Coffee coffee = factory.createCoffee();
coffee.addMilk();
coffee.addSugar();
return coffee;
}
}
测试类
/**
* @Autor: wl
* @Date: 2021/8/14 21:11
* @Version 1.0
*/
public class TestCoffee {
public static void main(String[] args) {
CoffeeStore store = new CoffeeStore();
store.setFactory(new LatteCoffeeFactory());
Coffee coffee = store.orderCoffer();
System.out.println(coffee.getName());
}
}
优点:
- 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程;
- 在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则;
缺点:
- 每新增一个产品,就要新增一个工厂实现类和对应产品类,这样增加了复杂度
抽象工厂模式
现咖啡店业务发生改变,不仅要生产咖啡还要生产甜点,如提拉米苏、抹茶慕斯等,要是按照工厂方法模式,需要定义提拉米苏类、抹茶慕斯类、提拉米苏工厂、抹茶慕斯工厂、甜点工厂类,很容易发生类爆炸情况。其中拿铁咖啡、美式咖啡是一个产品等级,都是咖啡;提拉米苏、抹茶慕斯也是一个产品等级;拿铁咖啡和提拉米苏是同一产品族(也就是都属于意大利风味),美式咖啡和抹茶慕斯是同一产品族(也就是都属于美式风味)。所以这个案例可以使用抽象工厂模式实现。类图如下:
具体实现代码:
抽象产品类
/**
* @Autor: wl
* @Date: 2021/8/16 16:21
* @Version 1.0
*/
public interface DessertFactory {
// 抽象工厂,提供创建咖啡和创建甜品的方法
Coffee createCoffee();
Dessert createDessert();
}
美式风味生产工厂类
/**
* @Autor: wl
* @Date: 2021/8/16 16:28
* @Version 1.0
*/
public class AmericanDessertFactory implements DessertFactory{
// 美式甜品工厂
// 创建咖啡的方法
@Override
public Coffee createCoffee() {
return new AmericanCoffee();
}
// 创建甜品的方法
@Override
public Dessert createDessert() {
return new MatchaMousse();
}
}
意大利风味生产工厂类
/**
* @Autor: wl
* @Date: 2021/8/16 16:37
* @Version 1.0
*/
public class ItalyDessertFactory implements DessertFactory {
//意大利甜品风味工厂
@Override
public Coffee createCoffee() {
return new LatteCoffee();
}
// 创建甜品方法
@Override
public Dessert createDessert() {
return new Tiramisu();
}
}
咖啡抽象类
/**
* @Autor: wl
* @Date: 2021/8/14 21:02
* @Version 1.0
*/
public abstract class Coffee {
public abstract String getName();
// 加糖
public void addSugar() {
System.out.println("加糖");
}
// 加奶
public void addMilk() {
System.out.println("加奶");
}
}
美式风味实体类
/**
* @Autor: wl
* @Date: 2021/8/14 21:07
* @Version 1.0
*/
public class AmericanCoffee extends Coffee {
@Override
public String getName() {
return "美式咖啡";
}
}
/**
* @Autor: wl
* @Date: 2021/8/16 16:34
* @Version 1.0
*/
public class MatchaMousse extends Dessert{
@Override
public void show() {
System.out.println("抹茶莫斯");
}
}
意式风味实体类
/**
* @Autor: wl
* @Date: 2021/8/14 21:05
* @Version 1.0
*/
public class LatteCoffee extends Coffee{
@Override
public String getName() {
return "拿铁咖啡";
}
}
/**
* @Autor: wl
* @Date: 2021/8/16 16:35
* @Version 1.0
*/
public class Tiramisu extends Dessert {
@Override
void show() {
System.out.println("提拉米苏");
}
}
测试类
/**
* @Autor: wl
* @Date: 2021/8/16 16:43
* @Version 1.0
* @Describe 测试类
*/
public class Test {
public static void main(String[] args) {
DessertFactory factory = new ItalyDessertFactory();
Coffee coffee = factory.createCoffee();
System.out.println(coffee.getName());
factory.createDessert().show();
}
}
优点:
- 相比于工厂方法模式,抽象工厂模式适用于一个产品族,不再局限于一类产品了
缺点:
- 当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。
简单工厂模式+配置文件
可以通过工厂模式+配置文件的方式解除工厂对象和产品对象的耦合。在工厂类中加载配置文件中的全类名,并创建对象进行存储,客户端如果需要对象,直接进行获取即可。
具体的代码实现:
咖啡工厂:
/**
* @Autor: wl
* @Date: 2021/8/16 18:05
* @Version 1.0
* @Describe 简单工厂的改进和配置文件进行配合使用
*/
public class CoffeeFactory {
private static Map<String,Coffee> map=new HashMap<>();
static {
Properties properties = new Properties();
try {
// 加载配置文件
properties.load(CoffeeFactory.class.getClassLoader().getResourceAsStream("Beans.properties"));
// 读取配置文件里面的内容
Set<Object> keySet = properties.keySet();
for (Object o : keySet) {
// 获取全限定类名
String className = properties.getProperty((String) o);
Class clazz = Class.forName(className);
Coffee coffee = (Coffee) clazz.newInstance();
map.put((String)o,coffee);
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 根据名称获取对象
public static Coffee creatCoffee(String name){
return map.get(name);
}
}
咖啡抽象类
/**
* @Autor: wl
* @Date: 2021/8/14 21:02
* @Version 1.0
*/
public abstract class Coffee {
public abstract String getName();
// 加糖
public void addSugar() {
System.out.println("加糖");
}
// 加奶
public void addMilk() {
System.out.println("加奶");
}
}
美式咖啡
/**
* @Autor: wl
* @Date: 2021/8/14 21:07
* @Version 1.0
*/
public class AmericanCoffee extends Coffee {
@Override
public String getName() {
return "美式咖啡";
}
}
拿铁咖啡
/**
* @Autor: wl
* @Date: 2021/8/14 21:05
* @Version 1.0
*/
public class LatteCoffee extends Coffee {
@Override
public String getName() {
return "拿铁咖啡";
}
}
测试类
/**
* @Autor: wl
* @Date: 2021/8/16 18:25
* @Version 1.0
* @Describe 测试类
*/
public class Test {
public static void main(String[] args) {
Coffee coffee = CoffeeFactory.creatCoffee("american");
System.out.println(coffee.getName());
}
}
Peoperties配置文件:
american=com.wl.design.Factory.SingleFactory_config.AmericanCoffee
latte=com.wl.design.Factory.SingleFactory_config.LatteCoffee
jdk的工厂模式的实现-iterator
Collection接口是抽象工厂类,ArrayList是具体的工厂类;Iterator接口是抽象商品类,
ArrayList类中的Iter内部类是具体的商品类。在具体的工厂类中iterator()方法创建具体的商品
类的对象。
另:
- DateForamt类中的getInstance()方法使用的是工厂模式;
- Calendar类中的getInstance()方法使用的是工厂模式
原型模式
概念
用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象。
原型模式的克隆分为浅克隆和深克隆。
- 浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原
有属性所指向的对象的内存地址。 - 深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
Java中的Object类中提供了 clone() 方法来实现浅克隆。 Cloneable 接口是上面的类图中的抽
象原型类,而实现了Cloneable接口的子实现类就是具体的原型类
浅克隆
实体类
/**
* @Autor: wl
* @Date: 2021/8/17 11:14
* @Version 1.0
* @Describe 原型模式-浅拷贝
*/
public class People implements Cloneable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected People clone() throws CloneNotSupportedException {
return (People) super.clone();
}
public void show(){
System.out.println(name+"被颁发三好学生奖状");
}
}
测试类
/**
* @Autor: wl
* @Date: 2021/8/17 11:14
* @Version 1.0
* @Describe
*/
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
People people = new People();
people.setName("张三");
People people1 = people.clone();
people1.setName("李四");
people.show();
people1.show();
}
}
深克隆
使用序列化实现深克隆
实体类
/**
* @Autor: wl
* @Date: 2021/8/17 11:23
* @Version 1.0
* @Describe
*/
public class Student implements Serializable {
private String name;
public Student(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}
}
/**
* @Autor: wl
* @Date: 2021/8/17 11:25
* @Version 1.0
* @Describe
*/
public class People implements Cloneable, Serializable {
private Student student;
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
@Override
protected People clone() throws CloneNotSupportedException {
return (People) super.clone();
}
public void show(){
System.out.println(student.getName()+"被颁发三好学生奖状");
}
}
测试类
/**
* @Autor: wl
* @Date: 2021/8/17 11:28
* @Version 1.0
* @Describe
*/
public class Test {
public static void main(String[] args) throws Exception {
People people = new People();
people.setStudent(new Student("王五"));
// 输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\ideaProject\\design\\src\\main\\java\\com\\wl\\design\\3.txt"));
oos.writeObject(people);
// 输入流读取
ObjectInputStream ois=new ObjectInputStream( new FileInputStream("D:\\ideaProject\\design\\src\\main\\java\\com\\wl\\design\\3.txt"));
People people1 = (People) ois.readObject();
people1.getStudent().setName("123456");
people.show();
people1.show();
}
}
建造者模式
建造者模式-结构
-
抽象建造者类(Builder):这个接口规定要实现复杂对象的那些部分的创建,并不涉及具体的
部件对象的创建。 -
具体建造者类(ConcreteBuilder):实现 Builder 接口,完成复杂产品的各个部件的具体
创建方法。在构造过程完成后,提供产品的实例。 -
产品类(Product):要创建的复杂对象。
-
指挥者类(Director):调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产
品的信息,只负责保证对象各部分完整创建或按某种顺序创建。
具体的代码实现:
抽象建造者类(Builder):
public abstract class Builder {
// 提供给子类使用
protected Bike bike=new Bike();
// 建造车架
abstract void buildFrame();
// 建造车座
abstract void buildSeat();
// 创建自行车
abstract Bike createBike();
}
具体建造者类(ConcreteBuilder):
public class MobikeBuilder extends Builder {
@Override
void buildFrame() {
bike.setFrame("膜拜车架");
}
@Override
void buildSeat() {
bike.setSeat("膜拜座椅");
}
@Override
Bike createBike() {
return bike;
}
}
public class OfoBuilder extends Builder {
@Override
void buildFrame() {
bike.setFrame("小黄车车架");
}
@Override
void buildSeat() {
bike.setSeat("小黄车座椅");
}
@Override
Bike createBike() {
return bike;
}
}
产品类(Product):
public class Bike {
// 车架
private String frame;
// 车椅
private String seat;
public String getFrame() {
return frame;
}
public void setFrame(String frame) {
this.frame = frame;
}
public String getSeat() {
return seat;
}
public void setSeat(String seat) {
this.seat = seat;
}
@Override
public String toString() {
return "Bike{" +
"frame='" + frame + '\'' +
", seat='" + seat + '\'' +
'}';
}
}
指挥者类(Director):
public class Director {
private Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
public Bike construct(){
builder.buildFrame();
builder.buildSeat();
return builder.createBike();
}
}
测试类(用户类):
public class Test {
public static void main(String[] args) {
// 调用指挥类进
Director director = new Director(new MobikeBuilder());
// 调用指挥类中的建造方法
Bike bike = director.construct();
System.out.println(bike.getFrame());
}
}
优点:
- 建造者模式很容易进行扩展。如果有新的需求,通过实现一个新的建造者类就可以完成,基本上不用修改之前已经测试通过的代码,因此也就不会对原有功能引入风险。符合开闭原则
缺点:
- 建造者模式所创建的产品一般都具有较多的相同点,比如摩拜单车和哈喽单车都具有很多相同的组件,如果产品之间差异很大,不适合用建造者模式。
在开发中还有一个常用的使用方式,就是当一个类构造器需要传入很多参数时,如果创建这个类的实例,代码可读性会非常差,而且很容易引入错误,此时就可以利用建造者模式进行重构
建造者类:
public class Computer {
private String mainboard;
private String cpu;
private String screen;
private String keyword;
private String name;
private Computer (ComputerBuilder builder){
this.screen=builder.screen;
this.mainboard=builder.mainboard;
this.cpu=builder.cpu;
this.keyword=builder.keyword;
this.name=builder.name;
}
@Override
public String toString() {
return "Computer{" +
"mainboard='" + mainboard + '\'' +
", cpu='" + cpu + '\'' +
", screen='" + screen + '\'' +
", keyword='" + keyword + '\'' +
", name='" + name + '\'' +
'}';
}
// 使用建造者类 静态内部类
public static final class ComputerBuilder{
private String mainboard;
private String cpu;
private String screen;
private String keyword;
private String name;
public ComputerBuilder() {
}
public ComputerBuilder mainboard(String mainboard){
this.mainboard=mainboard;
return this;
}
public ComputerBuilder cpu(String cpu){
this.cpu=cpu;
return this;
}
public ComputerBuilder screen(String screen){
this.screen=screen;
return this;
}
public ComputerBuilder keyword(String keyword){
this.keyword=keyword;
return this;
}
public ComputerBuilder name(String name){
this.name=name;
return this;
}
// 使用构建者创建 Computer对象
public Computer build(){
return new Computer(this);
}
}
}
测试类:
public class Test {
public static void main(String[] args) {
// 这样就可以使用链式编程 可以根据自己想要的进行构建
Computer computer=new Computer.ComputerBuilder()
.cpu("intel")
.mainboard("联想")
.build();
}
}
小结
工厂方法模式vs建造者模式
-
工厂方法模式注重的是整体对象的创建方式,而建造者模式注重的是部件构建的过程,意在一步一步地精确构造创建出一个复杂的对象。
-
举个简单的例子,在生活中,我们要构建一辆汽车,工厂模式会将对应的汽车交给对应的厂商进行创建,而建造者模式是注重一个个组件拼接起来一辆汽车。java语言就好比如工厂模式,c++就像是建造者模式。
2.5.2 抽象工厂方法模式vs建造者模式
-
抽象工厂不关心构建过程,只关心什么产品由什么工厂生产即可,建造者模式则是要求按照指定的蓝图构造产品,它的主要目的是通过组装配件生产一个新的产品。
-
如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车
结构型模式
代理模式
代理模式在我们的生活中处处可见,例如我们租房子不是直接找房东而是找中介,买手机找对应的代理商,去对应的店铺买等等
代理模式-静态代理
如果要买火车票的话,需要去火车站买票,坐车到火车站,排队等一系列的操作,显然比较麻烦。而火车站在多个地方都有代售点,我们去代售点买票就方便很多了。这个例子其实就是典型的代理模式,火车站是目标对象,代售点是代理对象。
卖票接口:
/**
* @Autor: wl
* @Date: 2021/8/19 15:56
* @Version 1.0
* @Describe 卖票接口
*/
public interface SellTickets {
// 卖票方法
int sellTicket();
}
目标类:
/**
* @Autor: wl
* @Date: 2021/8/19 15:57
* @Version 1.0
* @Describe 目标类-火车站
*/
public class Station implements SellTickets{
@Override
public int sellTicket() {
// 火车站是目标类对象,假设一张票卖20
System.out.println("卖票");
return 20;
}
}
代理类:
/**
* @Autor: wl
* @Date: 2021/8/19 15:58
* @Version 1.0
* @Describe 静态代理类-火车票代售点
*/
public class StaticProxy implements SellTickets{
// 目标类
private Station station=new Station();
// 火车站的售票
@Override
public int sellTicket() {
int price = station.sellTicket();
price+=10;
System.out.println("代售点多收了10块服务费");
return price;
}
}
测试类:
/**
* @Autor: wl
* @Date: 2021/8/19 16:01
* @Version 1.0
* @Describe 测试类
*/
public class Test {
public static void main(String[] args) {
// 调用代理类
StaticProxy staticProxy = new StaticProxy();
int price = staticProxy.sellTicket();
System.out.println(price);
}
}
代理模式-JDK动态代理(如果没有接口,不能使用jdk动态代理)
接口:
/**
* @Autor: wl
* @Date: 2021/8/19 16:16
* @Version 1.0
* @Describe 卖票类
*/
public interface SellTickets {
// 卖票方法
int sellTicket();
}
代理类:
/**
* @Autor: wl
* @Date: 2021/8/19 15:58
* @Version 1.0
* @Describe 静态代理类-火车票代售点
*/
public class JDKProxy implements InvocationHandler {
// 目标类对象
private Object target = null;
public JDKProxy(Object target) {
this.target = target;
}
// 代理类
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*
Object proxy 代理对象
Method method 对应于在代理对象上调用的接口方法的 Method 实 例
Object[] args 代理对象调用接口方法时传递的实际参数
*/
Object result = method.invoke(target, args);
// 方法的增强
if (result!=null){
int price=(Integer) result;
price+=10;
result=price;
}
System.out.println("代理多收10块钱");
return result;
}
}
目标类
/**
* @Autor: wl
* @Date: 2021/8/19 15:57
* @Version 1.0
* @Describe 目标类-火车站
*/
public class Station implements SellTickets{
@Override
public int sellTicket() {
// 火车站是目标类对象,假设一张票卖20
System.out.println("卖票");
return 20;
}
}
测试类
/**
* @Autor: wl
* @Date: 2021/8/19 16:36
* @Version 1.0
* @Describe 测试类
*/
public class Test {
public static void main(String[] args) {
/*
ClassLoader loader, 类加载器
Class<?>[] interfaces, 接口的字节码对象
InvocationHandler h 代理类
*/
Station station = new Station();
// 动态代理类
JDKProxy proxy = new JDKProxy(station);
SellTickets st = (SellTickets) Proxy.newProxyInstance(station.getClass().getClassLoader(), station.getClass().getInterfaces(),proxy);
int price = st.sellTicket();
System.out.println(price);
}
}
代理模式-CGLIB动态代理(对jdk动态代理的补充,适用于没接口的)
此处省略1000行代码
适配器模式
概念
在生活中,我们要给手机充电,发现只有一个三孔插座,而我们的手机是双孔插座,这个时候我们又希望使用这个插座充电,这个时候我们会用到一个适配插座来相互适配。如图:
结构
适配器模式(Adapter)包含以下主要角色:
-
目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。
-
适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。
-
适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。
适配器模式-类适配器
现有一台电脑只能读取SD卡,而要读取TF卡中的内容的话就需要使用到适配器模式。创建一个读卡
器,将TF卡中的内容读取出来:
目标接口
SDCard:
/**
* @Autor: wl
* @Date: 2021/8/20 16:25
* @Version 1.0
* @Describe SDCard接口
*/
public interface SDCard {
// 读卡
String readSDCard();
// 写卡
void writeSDCard(String msg);
}
TFCard:
/**
* @Autor: wl
* @Date: 2021/8/20 16:22
* @Version 1.0
* @Describe TFCard接口
*/
public interface TFCard {
// 读卡
String readTFCard();
// 写卡
void writeTFCard(String msg);
}
适配者
TFCardImpl:
/**
* @Autor: wl
* @Date: 2021/8/20 16:49
* @Version 1.0
* @Describe TFCard具体的实现类
*/
public class TFCardImpl implements TFCard {
@Override
public String readTFCard() {
String msg="read TFCard";
return msg;
}
@Override
public void writeTFCard(String msg) {
System.out.println(msg);
}
}
SDCardImpl:
/**
* @Autor: wl
* @Date: 2021/8/20 16:48
* @Version 1.0
* @Describe SDCard的具体实现类
*/
public class SDCardImpl implements SDCard {
@Override
public String readSDCard() {
String msg="read sdCard";
return msg;
}
@Override
public void writeSDCard(String msg) {
System.out.println(msg);
}
}
computer:
/**
* @Autor: wl
* @Date: 2021/8/20 16:52
* @Version 1.0
* @Describe Computer读卡类,只能读取SD卡
*/
public class Computer {
public String readSDCard(SDCard sdCard){
if (sdCard==null){
throw new NullPointerException("sd card null");
}
return sdCard.readSDCard();
}
}
适配器类
/**
* @Autor: wl
* @Date: 2021/8/20 16:53
* @Version 1.0
* @Describe 适配器类
*/
public class Adapter extends TFCardImpl implements SDCard{
// 适配器类将本来电脑读不到TF卡的内容的数据,进行处理,从来让电脑读到TF卡的内容
@Override
public String readSDCard() {
System.out.println("adpater read");
return readTFCard();
}
@Override
public void writeSDCard(String msg) {
System.out.println("adpater write");
writeTFCard(msg);
}
}
测试类
/**
* @Autor: wl
* @Date: 2021/8/20 16:58
* @Version 1.0
* @Describe
*/
public class Test {
public static void main(String[] args) {
Computer computer = new Computer();
String msg = computer.readSDCard(new Adapter());
System.out.println(msg);
}
}
类适配器违背了合成复用原则。
适配器模式-对象适配器
目标接口
SDCard:
/**
* @Autor: wl
* @Date: 2021/8/20 16:25
* @Version 1.0
* @Describe SDCard接口
*/
public interface SDCard {
// 读卡
String readSDCard();
// 写卡
void writeSDCard(String msg);
}
TFCard:
/**
* @Autor: wl
* @Date: 2021/8/20 16:22
* @Version 1.0
* @Describe TFCard接口
*/
public interface TFCard {
// 读卡
String readTFCard();
// 写卡
void writeTFCard(String msg);
}
适配者
TFCardImpl:
/**
* @Autor: wl
* @Date: 2021/8/20 16:49
* @Version 1.0
* @Describe TFCard具体的实现类
*/
public class TFCardImpl implements TFCard {
@Override
public String readTFCard() {
String msg="read TFCard";
return msg;
}
@Override
public void writeTFCard(String msg) {
System.out.println(msg);
}
}
SDCardImpl:
/**
* @Autor: wl
* @Date: 2021/8/20 16:48
* @Version 1.0
* @Describe SDCard的具体实现类
*/
public class SDCardImpl implements SDCard {
@Override
public String readSDCard() {
String msg="read sdCard";
return msg;
}
@Override
public void writeSDCard(String msg) {
System.out.println(msg);
}
}
computer:
/**
* @Autor: wl
* @Date: 2021/8/20 16:52
* @Version 1.0
* @Describe Computer读卡类,只能读取SD卡
*/
public class Computer {
public String readSDCard(SDCard sdCard){
if (sdCard==null){
throw new NullPointerException("sd card null");
}
return sdCard.readSDCard();
}
}
适配器类
/**
* @Autor: wl
* @Date: 2021/8/20 16:53
* @Version 1.0
* @Describe 适配器类
*/
public class Adapter implements SDCard {
// 适配器类将本来电脑读不到TF卡的内容的数据,进行处理,从来让电脑读到TF卡的内容
private TFCard tfCard;
// 将对象传进来 避免违背合成复用原则
public Adapter(TFCard tfCard) {
this.tfCard = tfCard;
}
@Override
public String readSDCard() {
System.out.println("adpater read");
return tfCard.readTFCard();
}
@Override
public void writeSDCard(String msg) {
System.out.println("adpater write");
tfCard.writeTFCard(msg);
}
}
测试类
/**
* @Autor: wl
* @Date: 2021/8/20 16:58
* @Version 1.0
* @Describe
*/
public class Test {
public static void main(String[] args) {
Computer computer = new Computer();
// 创建 TFCard 读卡
TFCard tfCard = new TFCardImpl();
// 创建适配器类
Adapter adapter = new Adapter(tfCard);
String msg = computer.readSDCard(adapter);
System.out.println(msg);
}
}
JDK中Reader(字符流)、InputStream(字节流)的适配使用的是InputStreamReader。
装饰者模式
快餐店有炒面、炒饭这些快餐,可以额外附加鸡蛋、火腿、培根这些配菜,当然加配菜需要额外加钱每个配菜的价钱通常不太一样,那么计算总价就会显得比较麻烦。
使用继承的方式存在的问题:
- 扩展性不好
- 如果要再加一种配料(火腿肠),我们就会发现需要给FriedRice和FriedNoodles分别定义一个子类。如果要新增一个快餐品类(炒河粉)的话,就需要定义更多的子类。产生过多的子类
这个时候就可以使用装饰者模式,装饰者模式结构如下:
装饰(Decorator)模式中的角色:
-
抽象构件(Component)角色 :定义一个抽象接口以规范准备接收附加责任的对象。
-
具体构件(Concrete Component)角色 :实现抽象构件,通过装饰角色为其添加一些职责。
-
抽象装饰(Decorator)角色 : 继承或实现抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
-
具体装饰(ConcreteDecorator)角色 :实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。
抽象构件:
/**
* @Autor: wl
* @Date: 2021/8/21 20:04
* @Version 1.0
* @Describe 抽象构件-快餐店
*/
public abstract class FastFood {
// 价格
private float price;
// 描述
private String desc;
public FastFood() {
}
public FastFood(float price, String desc) {
this.price = price;
this.desc = desc;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
// 定义抽象方法 计算价格
public abstract float cost();
}
具体构件:
/**
* @Autor: wl
* @Date: 2021/8/21 20:08
* @Version 1.0
* @Describe 具体构件-炒饭
*/
public class FastRice extends FastFood{
public FastRice(){
// 使用父类中的构造方法给属性填充
super(12,"扬州炒饭");
}
// 计算价格
@Override
public float cost() {
return getPrice();
}
}
抽象装饰:
/**
* @Autor: wl
* @Date: 2021/8/21 20:30
* @Version 1.0
* @Describe 抽象装饰-搭配
*/
public abstract class Decorator extends FastFood {
private FastFood fastFood;
public FastFood getFastFood() {
return fastFood;
}
public void setFastFood(FastFood fastFood) {
this.fastFood = fastFood;
}
public Decorator(FastFood fastFood,float price,String desc){
super(price,desc);
this.fastFood=fastFood;
}
}
具体装饰:
/**
* @Autor: wl
* @Date: 2021/8/21 20:37
* @Version 1.0
* @Describe 具体装饰-鸡蛋类
*/
public class Egg extends Decorator {
public Egg(FastFood fastFood) {
super(fastFood,1,"鸡蛋");
}
@Override
public float cost() {
return getPrice()+getFastFood().getPrice();
}
@Override
public String getDesc() {
return super.getDesc() + getFastFood().getDesc();
}
}
/**
* @Autor: wl
* @Date: 2021/8/21 20:49
* @Version 1.0
* @Describe 具体装饰-培根
*/
public class Bacon extends Decorator{
public Bacon(FastFood fastFood) {
super(fastFood,2,"培根");
}
@Override
public float cost() {
return getPrice()+getFastFood().getPrice();
}
}
好处:
-
装饰者模式可以带来比继承更加灵活性的扩展功能,使用更加方便,可以通过组合不同的装饰者对象来获取具有不同行为状态的多样化的结果。装饰者模式比继承更具良好的扩展性,完美的遵循开闭原则,继承是静态的附加责任,装饰者则是动态的附加责任。
-
装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
jdk源码中BufferedWriter使用装饰者模式对Writer子实现类进行了增强,添加了缓冲区,提高了写据的效率。
和代理模式不同的是,代理模式是对目标类的保护和隐藏,装饰者模式是对目标类的增强。
桥接模式
需要开发一个跨平台视频播放器,可以在不同操作系统平台(如Windows、Mac、Linux等)上播放多种格式的视频文件,常见的视频格式包括RMVB、AVI、WMV等。该播放器包含了两个维度,适合使用桥接模式
文件格式接口:
/**
* @Autor: wl
* @Date: 2021/8/22 20:07
* @Version 1.0
* @Describe 抽象化角色-文件格式
*/
public interface VideoFile {
void decode(String filename);
}
具体实现类:
/**
* @Autor: wl
* @Date: 2021/8/22 20:10
* @Version 1.0
* @Describe
*/
public class AVIFile implements VideoFile {
@Override
public void decode(String filename) {
System.out.println("AVI 文件名为:"+filename);
}
}
/**
* @Autor: wl
* @Date: 2021/8/22 20:09
* @Version 1.0
* @Describe
*/
public class RmvbFile implements VideoFile {
@Override
public void decode(String filename) {
System.out.println("Rmvb文件名为:"+filename);
}
}
操作系统抽象类:
/**
* @Autor: wl
* @Date: 2021/8/22 20:11
* @Version 1.0
* @Describe
*/
public abstract class OperatingSystem {
protected VideoFile videoFile;
public OperatingSystem(VideoFile videoFile) {
this.videoFile = videoFile;
}
public abstract void play(String filename);
}
操作系统具体实现类:
/**
* @Autor: wl
* @Date: 2021/8/22 20:12
* @Version 1.0
* @Describe
*/
public class Windows extends OperatingSystem {
public Windows(VideoFile videoFile) {
super(videoFile);
}
@Override
public void play(String filename) {
videoFile.decode(filename);
}
}
/**
* @Autor: wl
* @Date: 2021/8/22 20:12
* @Version 1.0
* @Describe
*/
public class Mac extends OperatingSystem {
public Mac(VideoFile videoFile) {
super(videoFile);
}
@Override
public void play(String filename) {
videoFile.decode(filename);
}
}
测试类:
/**
* @Autor: wl
* @Date: 2021/8/22 20:15
* @Version 1.0
* @Describe
*/
public class Test {
public static void main(String[] args) {
OperatingSystem os=new Windows(new RmvbFile());
os.play("战狼");
}
}
使用场景:
- 当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时。
- 当一个系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加时
外观模式
外观(Facade)模式包含以下主要角色:
- 外观(Facade)角色:为多个子系统对外提供一个共同的接口。
- 子系统(Sub System)角色:实现系统的部分功能,客户可以通过外观角色访问它
案例
小明的爷爷已经60岁了,一个人在家生活:每次都需要打开灯、打开电视、打开空调;睡觉时关灯、关闭电视、关闭空调;操作起来都比较麻烦。所以小明给爷爷买了智能音箱,可以通过语音直接控制这些智能家电的开启和关闭:
子系统类:
/**
* @Autor: wl
* @Date: 2021/8/22 20:55
* @Version 1.0
* @Describe 子系统(Sub System)角色-空调
*/
public class AirCondition {
public void on(){
System.out.println("空调已经打开");
}
public void off(){
System.out.println("空调已经关闭");
}
}
/**
* @Autor: wl
* @Date: 2021/8/22 20:53
* @Version 1.0
* @Describe 子系统(Sub System)角色-电灯
*/
public class Light {
public void on(){
System.out.println("电灯已经打开");
}
public void off(){
System.out.println("电灯已经关闭");
}
}
/**
* @Autor: wl
* @Date: 2021/8/22 20:53
* @Version 1.0
* @Describe 子系统(Sub System)角色-电视机
*/
public class TV {
public void on(){
System.out.println("电视机已经打开");
}
public void off(){
System.out.println("电视机已经关闭");
}
}
外观类:
/**
* @Autor: wl
* @Date: 2021/8/22 20:58
* @Version 1.0
* @Describe 外观类-用户访问的类
*/
public class SmartApplication {
private TV tv;
private Light light;
private AirCondition airCondition;
public SmartApplication() {
this.tv = new TV();
this.light = new Light();
this.airCondition = new AirCondition();
}
public void controller(String message) {
if ("打开".contains(message)) {
on();
} else if ("关闭".contains(message)) {
off();
} else {
System.out.println("我还不能听懂你的意思");
}
}
private void on() {
tv.on();
airCondition.on();
light.on();
}
private void off() {
tv.off();
airCondition.off();
light.off();
}
}
测试类:
/**
* @Autor: wl
* @Date: 2021/8/22 21:02
* @Version 1.0
* @Describe
*/
public class Test {
public static void main(String[] args) {
SmartApplication smartApplication = new SmartApplication();
smartApplication.controller("打开");
System.out.println("================================");
smartApplication.controller("关闭");
}
}
好处:
- 降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响调用它的客户类。
- 对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易。
缺点:
- 不符合开闭原则,修改很麻烦
享元模式
运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似对象的开销,从而提高系统资源的利用率。
案例
下面的图片是众所周知的俄罗斯方块中的一个个方块,如果在俄罗斯方块这个游戏中,每个不同的方块都是一个实例对象,这些对象就要占用很多的内存空间,下面利用享元模式进行实现。
类图:
抽象享元角色:
/**
* @Autor: wl
* @Date: 2021/8/26 15:06
* @Version 1.0
* @Describe 抽象享元角色
*/
public abstract class AbstractBox {
public abstract String getShape();
public void display(String color){
System.out.println("方块形状:" + this.getShape() + " 颜色:" + color);
}
}
享元工厂:
/**
* @Autor: wl
* @Date: 2021/8/26 15:32
* @Version 1.0
* @Describe 享元工厂
*/
public class BoxFactory {
private HashMap<String,AbstractBox> map;
private BoxFactory(){
map=new HashMap<>();
map.put("L",new LBox());
map.put("I",new IBox());
map.put("Z",new ZBox());
}
private static BoxFactory boxFactory=new BoxFactory();
public static BoxFactory getBoxFactory() {
return boxFactory;
}
public AbstractBox getBox(String key){
return map.get(key);
}
}
具体享元角色:
/**
* @Autor: wl
* @Date: 2021/8/26 15:27
* @Version 1.0
* @Describe 具体享元角色
*/
public class IBox extends AbstractBox {
@Override
public String getShape() {
return "i形状";
}
}
/**
* @Autor: wl
* @Date: 2021/8/26 15:27
* @Version 1.0
* @Describe
*/
public class LBox extends AbstractBox {
@Override
public String getShape() {
return "L形状";
}
}
/**
* @Autor: wl
* @Date: 2021/8/26 15:27
* @Version 1.0
* @Describe
*/
public class ZBox extends AbstractBox {
@Override
public String getShape() {
return "Z形状";
}
}
测试类:
/**
* @Autor: wl
* @Date: 2021/8/26 15:35
* @Version 1.0
* @Describe
*/
public class Test {
public static void main(String[] args) {
BoxFactory boxFactory = BoxFactory.getBoxFactory();
AbstractBox l = boxFactory.getBox("L");
l.display("红色");
AbstractBox i = boxFactory.getBox("I");
i.display("红色");
}
}
优点:
-
极大减少了内存中相同或相似对象数量,节约系统资源。
-
享元状态中得外部状态相对独立,且不影响内部状态。
缺点:
- 为了使对象可以共享,需要将享元对象的部分状态外部化,分离内部状态和外部状态,使程序逻辑复杂
使用场景:
-
一个系统有大量相同或者相似的对象,造成内存的大量耗费。
-
对象的大部分状态都可以外部化,可以将这些外部状态传入对象中。
-
在使用享元模式时需要维护一个存储享元对象的享元池,而这需要耗费一定的系统资源,因此,应当在需要多次重复使用享元对象时才值得使用享元模式。
JDK使用享元模式的类是Integer
行为型设计模式
行为型设计模式-模板方法模式
模板方法(Template Method)模式包含以下主要角色:
-
抽象类(Abstract Class):负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基
本方法构成。 -
模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。
-
基本方法:是实现算法各个步骤的方法,是模板方法的组成部分。基本方法又可以分为三种:
-
抽象方法(Abstract Method) :一个抽象方法由抽象类声明、由其具体子类实现。
-
具体方法(Concrete Method) :一个具体方法由一个抽象类或具体类声明并实现,其子类可以进行覆盖也可以直接继承。
-
钩子方法(Hook Method) :在抽象类中已经实现,包括用于判断的逻辑方法和需要
子类重写的空方法两种。一般钩子方法是用于判断的逻辑方法,这类方法名一般为isXxx,返回值类型为boolean类型。 -
具体子类(Concrete Class):实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的组成步骤。
炒菜的步骤是固定的,分为倒油、热油、倒蔬菜、倒调料品、翻炒等步骤。现通过模板方法模式来用代码模拟:
-
抽象方法: AbstractClass:
/**
* @Autor: wl
* @Date: 2021/8/26 19:04
* @Version 1.0
* @Describe 抽象类(定义模板方法和基本方法)
*/
public abstract class AbstractClass {
public final void cookie() {
pullOil();
UpOil();
addSeason();
addVegetable();
fry();
}
// 具体的方法
protected void pullOil() {
System.out.println("倒油");
}
protected void UpOil() {
System.out.println("热油");
}
protected void fry() {
System.out.println("炒啊炒啊炒");
}
// 抽象方法
abstract void addSeason();
abstract void addVegetable();
}
具体子类:
/**
* @Autor: wl
* @Date: 2021/8/27 0:21
* @Version 1.0
* @Describe
*/
public class PotatoSence extends AbstractClass{
@Override
void addSeason() {
System.out.println("加醋");
}
@Override
void addVegetable() {
System.out.println("加土豆");
}
}
/**
* @Autor: wl
* @Date: 2021/8/27 0:23
* @Version 1.0
* @Describe
*/
public class tomato extends AbstractClass {
@Override
void addSeason() {
System.out.println("加盐");
}
@Override
void addVegetable() {
System.out.println("加土豆");
}
}
测试类:
public class Test {
public static void main(String[] args) {
PotatoSence ps = new PotatoSence();
ps.cookie();
}
}
优点:
-提高代码的复用性
- 实现了反向控制。通过一个父类调用其子类的操作,通过对子类的具体实现扩展不同的行为,实现了反向控制。
缺点:
- 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象
适用场景:
- 算法整体步骤比较稳定,但是其中个别比较易变,这个时候可以使用模板方法模式,将易变的 部分抽象起来,供子类实现。
JDK源码中使用到的模板方法
InputStream类就使用了模板方法模式。在InputStream类中定义了多个 read() 方法。在InputStream父类中已经定义好了读取一个字节数组数据的方法是每次读取一个字节,
并将其存储到数组的第一个索引位置,读取len个字节数据。具体如何读取一个字节数据呢?由子类实
现
行为型设计模式-策略模式
结构如下:
策略模式的主要角色如下:
-
抽象策略(Strategy)类:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有
的具体策略类所需的接口。 -
具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现或行为。
-
环境(Context)类:持有一个策略类的引用,最终给客户端调用。
抽象策略:
public interface Strategy {
void show();
}
具体的策略角色:
public class StrategyA implements Strategy {
@Override
public void show() {
System.out.println("买一送二");
}
}
public class StrategyB implements Strategy {
@Override
public void show() {
System.out.println("满1000元减200");
}
}
public class StrategyC implements Strategy {
@Override
public void show() {
System.out.println("300元购物卷");
}
}
环境角色:
public class SaleMan {
private Strategy strategy;
public SaleMan(Strategy strategy) {
this.strategy = strategy;
}
public void saleShow(){
strategy.show();
}
}
测试角色
public class Test {
public static void main(String[] args) {
SaleMan saleManA = new SaleMan(new StrategyA());
saleManA.saleShow();
SaleMan saleManB = new SaleMan(new StrategyB());
saleManB.saleShow();
SaleMan saleManC = new SaleMan(new StrategyC());
saleManC.saleShow();
}
}
优点:
-
策略类之间可以自由切换,由于策略类都实现同一个接口,所以是他们之间可以自由切换
-
易于扩展,增加一个新的策略只需要添加一个具体的策略类即可,不用修改原有的代码,并符合开闭原则
-
避免使用多重条件选择语句(if else),充分体现出面向对象设计原则
缺点:
-
客户端必须知道所有的策略类.并自己选择一个策略类
-
策略模式将造成产生很多策略类,可以通过使用享元模式在一定程度上减少对象的数量
使用场景:
-
在(if,else)语句过多的情况下
-
一个系统需要动态的在几个算法中选择一种时候,可将每个算法封装到策略类中
行为型设计模式-命令模式
将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。
命令模式包含以下主要角色:
-
抽象命令类(Command)角色: 定义命令的接口,声明执行的方法。
-
具体命令(Concrete Command)角色:具体的命令,实现命令接口;通常会持有接收者,并调
用接收者的功能来完成命令要执行的操作。 -
实现者/接收者(Receiver)角色: 接收者,真正执行命令的对象。任何类都可能成为一个接收
者,只要它能够实现命令要求实现的相应功能。 -
调用者/请求者(Invoker)角色: 要求命令对象执行请求,通常会持有命令对象,可以持有很
多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。
案例:
服务员
: 就是调用者角色,由她来发起命令。
资深大厨
: 就是接收者角色,真正命令执行的对象。
订单
: 命令中包含订单。
类图:
抽象命令:
/**
* @Autor: wl
* @Date: 2021-09-01 10:06
* @Version 1.0
* @Describe 抽象命令
*/
public interface Command {
void execute();
}
具体命令:
/**
* @Autor: wl
* @Date: 2021-09-01 10:07
* @Version 1.0
* @Describe 具体命令
*/
public class OrderCommand implements Command {
// 持有接收者对象
private SeniorChef seniorChef;
private Order order;
public OrderCommand(SeniorChef seniorChef, Order order) {
this.seniorChef = seniorChef;
this.order = order;
}
@Override
public void execute() {
System.out.println(order.getDiningTable() + "号卓的订单:");
Map<String, Integer> foodMap = order.getFoodDic();
foodMap.forEach(
(k, v) -> {
seniorChef.makeFood(v,k);
}
);
// 模拟真实
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(order.getDiningTable() + "卓的订单已经完成");
}
}
/**
* @Autor: wl
* @Date: 2021-09-01 10:12
* @Version 1.0
* @Describe
*/
public class Order {
// 餐桌号码
private String diningTable;
// 用来存储餐名并记录份数
private Map<String,Integer> foodDic=new HashMap<>();
public String getDiningTable() {
return diningTable;
}
public void setDiningTable(String diningTable) {
this.diningTable = diningTable;
}
public Map<String, Integer> getFoodDic() {
return foodDic;
}
public void setFood(String foodName,int num) {
foodDic.put(foodName,num);
}
}
发出命令对象:
/**
* @Autor: wl
* @Date: 2021-09-01 16:57
* @Version 1.0
* @Describe 发出命令的对象
*/
public class Waitor {
private ArrayList<Command> commands;
public Waitor() {
commands = new ArrayList<>();
}
public void setCommand(Command cmd){
commands.add(cmd);
}
// 发出命令
public void orderUp(){
System.out.println("服务员:订单来了");
for (Command command : commands) {
if (command!=null){
command.execute();
}
}
}
}
接收命令对象:
/**
* @Autor: wl
* @Date: 2021-09-01 10:09
* @Version 1.0
* @Describe 接收者对象
*/
public class SeniorChef {
public void makeFood(int num,String foodName){
System.out.println(num+"份"+foodName);
}
}
优点:
-
降低耦合,能将调用对象和操作对象解耦合
-
增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,它满足“开闭原则”,对扩展比较灵活。
缺点:
-
使用命令模式会造成系统有过多的具体命令类
-
系统结构也会更加复杂
使用场景:
- 系统需要将请求者和执行者解耦合
- 系统支持命令的撤销和恢复操作
JDK源码中使用到的
:
Runable是一个典型命令模式,Runnable担当命令的角色,Thread充当的是调用者,start方法就
是其执行方法
行为型设计模式-责任链模式
职责链模式主要包含以下角色:
-
抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
-
具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请
求,如果可以处理请求则处理,否则将该请求转给它的后继者。 -
客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节
和请求的传递过程。
现需要开发一个请假流程控制系统。请假一天以下的假只需要小组长同意即可;请假1天到3天的假还需要部门经理同意;请求3天到7天还需要总经理同意才行。
类图如下:
抽象处理者:
/**
* @Autor: wl
* @Date: 2021-09-01 18:49
* @Version 1.0
* @Describe 抽象处理者
*/
public abstract class Handler {
protected static final int NUM_ONE = 1;
protected static final int NUM_THREE = 3;
protected static final int NUM_SEVEN = 7;
// 批假区间
private int numStart;
private int numEnd;
private Handler nextHandler;
public Handler(int numStart) {
this.numStart = numStart;
}
public Handler(int numStart, int numEnd) {
this.numStart = numStart;
this.numEnd = numEnd;
}
// 设置上级领导
public void setNextHandler(Handler nextHandler) {
this.nextHandler = nextHandler;
}
// 各级领导请求条的方法
public abstract void handle(LeaveRequest leaveRequest);
// 提交请假条
public final void submit(LeaveRequest leaveRequest) {
this.handle(leaveRequest);
if (this.nextHandler != null && leaveRequest.getNum() > this.numEnd) {
// 提交给上级领导审批
this.nextHandler.submit(leaveRequest);
} else {
System.out.println("流程结束");
}
}
}
具体处理者:
/**
* @Autor: wl
* @Date: 2021-09-01 19:05
* @Version 1.0
* @Describe 具体处理者-部门组长
*/
public class GroupLeader extends Handler {
public GroupLeader() {
super(0,NUM_ONE);
}
@Override
public void handle(LeaveRequest leaveRequest) {
System.out.println(leaveRequest.getName()+"请假"+leaveRequest.getNum()+"天,请假内容是"+leaveRequest.getContent());
System.out.println("同意请假");
System.out.println("审批人:");
System.out.println("部门组长张三");
}
}
/**
* @Autor: wl
* @Date: 2021-09-01 19:09
* @Version 1.0
* @Describe 具体处理者-部门经理
*/
public class Manager extends Handler {
public Manager() {
super(NUM_ONE,NUM_THREE);
}
@Override
public void handle(LeaveRequest leaveRequest) {
System.out.println(leaveRequest.getName()+"请假"+leaveRequest.getNum()+"天,请假内容是"+leaveRequest.getContent());
System.out.println("同意请假");
System.out.println("审批人:");
System.out.println("部门经理小王"); }
}
/**
* @Autor: wl
* @Date: 2021-09-01 19:09
* @Version 1.0
* @Describe 具体处理者-老板
*/
public class Boss extends Handler {
public Boss() {
super(NUM_THREE,NUM_SEVEN);
}
@Override
public void handle(LeaveRequest leaveRequest) {
System.out.println(leaveRequest.getName()+"请假"+leaveRequest.getNum()+"天,请假内容是"+leaveRequest.getContent());
System.out.println("同意请假:");
System.out.println("审批人:");
System.out.println("公司CEO大王");
}
}
请求类:
/**
* @Autor: wl
* @Date: 2021-09-01 18:45
* @Version 1.0
* @Describe 请求类
*/
public class LeaveRequest {
// 请假人姓名
private String name;
// 请假天数
private int num;
// 请假内容
private String content;
public LeaveRequest(String name, int num, String content) {
this.name = name;
this.num = num;
this.content = content;
}
public String getName() {
return name;
}
public int getNum() {
return num;
}
public String getContent() {
return content;
}
@Override
public String toString() {
return "LeaveRequest{" +
"name='" + name + '\'' +
", num=" + num +
", content='" + content + '\'' +
'}';
}
}
测试类:
/**
* @Autor: wl
* @Date: 2021-09-01 19:12
* @Version 1.0
* @Describe 测试类
*/
public class Test {
public static void main(String[] args) {
LeaveRequest leaveRequest = new LeaveRequest("新人小明", 1, "毕业答辩");
GroupLeader groupLeader = new GroupLeader();
Manager manager = new Manager();
Boss boss = new Boss();
// 设置领导等级
groupLeader.setNextHandler(manager);
manager.setNextHandler(boss);
// 小明提交请假 小组长先进行审批
groupLeader.submit(leaveRequest);
}
}
优点:
-
降低耦合度,请求者和接收者的耦合度降低
-
责任分担啊,每个类只处理自己的职责
-
增强了系统的可扩展性
缺点:
-
对于比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定的影响
-
不能保证每一个请求被处理,由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能会一直传到链的末端都得不到处理。
JDK源码的使用
:
在javaWeb应用开发中,FilterChain是职责链(过滤器)模式的典型应用
行为型设计模式-状态模式
抽象状态(State)角色:
/**
* @version v1.0
* @ClassName: LiftState
* @Description: 抽象状态类
* @Author: wl
*/
public abstract class LiftState {
//声明环境角色类变量
protected Context context;
public void setContext(Context context) {
this.context = context;
}
//电梯开启操作
public abstract void open();
//电梯关闭操作
public abstract void close();
//电梯运行操作
public abstract void run();
//电梯停止操作
public abstract void stop();
}
具体状态(Concrete State)角色:
/**
* @version v1.0
* @ClassName: OpeningState
* @Description: 电梯开启状态类
* @Author: wl
*/
public class OpeningState extends LiftState {
//当前状态要执行的方法
public void open() {
System.out.println("电梯开启。。。");
}
public void close() {
//修改状态
super.context.setLiftState(Context.CLOSING_STATE);
//调用当前状态中的context中的close方法
super.context.close();
}
public void run() {
//什么都不做
}
public void stop() {
//什么都不做
}
}
/**
* @version v1.0
* @ClassName: OpeningState
* @Description: 电梯关闭状态类
* @Author: wl
*/
public class ClosingState extends LiftState {
@Override
//电梯门关闭,这是关闭状态要实现的动作
public void close() {
System.out.println("电梯门关闭...");
}
//电梯门关了再打开,逗你玩呢,那这个允许呀
@Override
public void open() {
super.context.setLiftState(Context.OPENING_STATE);
super.context.open();
}
//电梯门关了就跑,这是再正常不过了
@Override
public void run() {
super.context.setLiftState(Context.RUNNING_STATE);
super.context.run();
}
//电梯门关着,我就不按楼层
@Override
public void stop() {
super.context.setLiftState(Context.STOPPING_STATE);
super.context.stop();
}
}
/**
* @version v1.0
* @ClassName: OpeningState
* @Description: 电梯停止状态类
* @Author: wl
*/
public class StoppingState extends LiftState {
//停止状态,开门,那是要的!
@Override
public void open() {
//状态修改
super.context.setLiftState(Context.OPENING_STATE);
//动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作
super.context.getLiftState().open();
}
@Override
public void close() {//虽然可以关门,但这个动作不归我执行
//状态修改
super.context.setLiftState(Context.CLOSING_STATE);
//动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作
super.context.getLiftState().close();
}
//停止状态再跑起来,正常的很
@Override
public void run() {
//状态修改
super.context.setLiftState(Context.RUNNING_STATE);
//动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作
super.context.getLiftState().run();
}
//停止状态是怎么发生的呢?当然是停止方法执行了
@Override
public void stop() {
System.out.println("电梯停止了...");
}
}
/**
* @version v1.0
* @ClassName: OpeningState
* @Description: 电梯运行状态类
* @Author: wl
*/
public class RunningState extends LiftState {
//运行的时候开电梯门?你疯了!电梯不会给你开的
@Override
public void open() {
//do nothing
}
//电梯门关闭?这是肯定了
@Override
public void close() {//虽然可以关门,但这个动作不归我执行
//do nothing
}
//这是在运行状态下要实现的方法
@Override
public void run() {
System.out.println("电梯正在运行...");
}
//这个事绝对是合理的,光运行不停止还有谁敢做这个电梯?!估计只有上帝了
@Override
public void stop() {
super.context.setLiftState(Context.STOPPING_STATE);
super.context.stop();
}
}
环境(Context)角色
/**
* @version v1.0
* @ClassName: Context
* @Description: 环境角色类
* @Author: wl
*/
public class Context {
//定义对应状态对象的常量
public final static OpeningState OPENING_STATE = new OpeningState();
public final static ClosingState CLOSING_STATE = new ClosingState();
public final static RunningState RUNNING_STATE = new RunningState();
public final static StoppingState STOPPING_STATE = new StoppingState();
//定义一个当前电梯状态变量
private LiftState liftState;
public LiftState getLiftState() {
return liftState;
}
//设置当前状态对象
public void setLiftState(LiftState liftState) {
this.liftState = liftState;
//设置当前状态对象中的Context对象
this.liftState.setContext(this);
}
public void open() {
this.liftState.open();
}
public void close() {
this.liftState.close();
}
public void run() {
this.liftState.run();
}
public void stop() {
this.liftState.stop();
}
}
测试类:
/**
* @version v1.0
* @ClassName: Client
* @Description: TODO(一句话描述该类的功能)
* @Author: wl
*/
public class Client {
public static void main(String[] args) {
//创建环境角色对象
Context context = new Context();
//设置当前电梯装填
context.setLiftState(new ClosingState());
context.open();
context.run();
context.close();
context.stop();
}
}
优点:
-
将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为
-
允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块
缺点:
-
状态模式的使用必然会增加系统类和对象的个数。
-
模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
-
模式对"开闭原则"的支持并不太好
使用场景:
-
个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式。
-
操作中含有庞大的分支结构,并且这些分支决定于对象的状态时。
行为型设计模式-观察者模式
又被称为发布-订阅(Publish/Subscribe)模式,它定义了一种一对多的依赖关系,让多个观察者
对象同时监听某一个主题对象
在使用微信公众号时,大家都会有这样的体验,当你关注的公众号中有新内容更新的话,它就会推送给关注公众号的微信用户端。我们使用观察者模式来模拟这样的场景,微信用户就是观察者,微信公众号是被观察者,有多个的微信用户关注了程序猿这个公众号。
代码省略.............
行为型设计模式-中介模式
现在租房基本都是通过房屋中介,房主将房屋托管给房屋中介,而租房者从房屋中介获取房屋信息。房屋中介充当租房者与房屋所有者之间的中介者
优点:
-
解耦合
-
集中控制交互
缺点:
- 当租客类太多,中介者的职责将会很大,他会变的复杂而庞大,以至于难以维护系统
行为型设计模式-迭代器模式
概念
提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
类图如下:
迭代器主要角色如下:
-
抽象聚合(Aggregate)角色:定义存储、添加、删除聚合元素以及创建迭代器对象的接口。
-
具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。
-
抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()、next() 等方法。
-
具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对
象的遍历,记录遍历的当前位置。
抽象迭代器角色:
/**
* @Autor: wl
* @Date: 2021-09-02 21:19
* @Version 1.0
* @Describe 抽象迭代器角色
*/
public interface StudentIterator {
boolean hasNext();
Student next();
}
具体迭代器角色:
/**
* @Autor: wl
* @Date: 2021-09-02 21:21
* @Version 1.0
* @Describe 迭代器角色
*/
public class StudentIteratorImpl implements StudentIterator {
private List<Student> list;
private int position=0;
public StudentIteratorImpl(List<Student> list) {
this.list = list;
}
// 判断是否还有下个元素
@Override
public boolean hasNext() {
return position<list.size();
}
// 得到学生对象
@Override
public Student next() {
Student student = list.get(position);
position++;
return student;
}
}
抽象聚合角色:
/**
* @Autor: wl
* @Date: 2021-09-02 21:24
* @Version 1.0
* @Describe 抽象聚合角色
*/
public interface StudentAggregate {
void addStudent(Student stu);
void removeStudent(Student stu);
StudentIterator getStudentIterator();
}
具体聚合角色:
/**
* @Autor: wl
* @Date: 2021-09-02 21:26
* @Version 1.0
* @Describe 具体聚合角色
*/
public class StudentAggregateImpl implements StudentAggregate {
private List<Student> list=new ArrayList<>();
@Override
public void addStudent(Student stu) {
list.add(stu);
}
@Override
public void removeStudent(Student stu) {
list.remove(stu);
}
@Override
public StudentIterator getStudentIterator() {
return new StudentIteratorImpl(list);
}
}
学生类对象:
/**
* @Autor: wl
* @Date: 2021-09-02 21:19
* @Version 1.0
* @Describe 学生类对象
*/
public class Student {
private String name;
private String num;
public Student(String name, String num) {
this.name = name;
this.num = num;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNum() {
return num;
}
public void setNum(String num) {
this.num = num;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", num='" + num + '\'' +
'}';
}
}
测试类:
/**
* @Autor: wl
* @Date: 2021-09-02 21:28
* @Version 1.0
* @Describe
*/
public class Test {
public static void main(String[] args) {
StudentAggregateImpl sa = new StudentAggregateImpl();
sa.addStudent(new Student("小王","1001"));
sa.addStudent(new Student("小李","1002"));
sa.addStudent(new Student("校长","1003"));
// 获取迭代器对象
StudentIterator studentIterator = sa.getStudentIterator();
while (studentIterator.hasNext()){
Student stu = studentIterator.next();
System.out.println(stu);
}
}
}
优点:
-
它支持以不同的方式遍历一个聚合对象,在同一个聚合对象上可以定义多种遍历方式
-
迭代器简化了聚合类。由于引入了迭代器,在原有的聚合对象中不需要再自行提供数据遍历等方法,这样可以简化聚合类的设计
缺点:
-
增加了类的个数,这在一定程度上增加了系统的复杂性
行为型设计模式-访问者模式
结构:
-
抽象访问者(Visitor)角色:定义了对每一个元素 (Element) 访问的行为,它的参数就是可
以访问的元素,它的方法个数理论上来讲与元素类个数(Element的实现类个数)是一样的,从
这点不难看出,访问者模式要求元素类的个数不能改变。 -
具体访问者(ConcreteVisitor)角色:给出对每一个元素类访问时所产生的具体行为。
-
抽象元素(Element)角色:定义了一个接受访问者的方法( accept ),其意义是指,每一个元素都要可以被访问者访问。
-
具体元素(ConcreteElement)角色: 提供接受访问方法的具体实现,而这个具体的实现,通
常情况下是使用访问者提供的访问该元素类的方法。 -
对象结构(Object Structure)角色:定义当中所提到的对象结构,对象结构是一个抽象表
述,具体点可以理解为一个具有容器性质或者复合对象特性的类,它会含有一组元素
( Element ),并且可以迭代这些元素,供访问者访问。
案例实现:
现在养宠物的人特别多,我们就以这个为例,当然宠物还分为狗,猫等,要给宠物喂食的话,主人可以喂,其他人也可以喂食。
类图如下:
抽象元素角色:
/**
* @Autor: wl
* @Date: 2021-09-02 22:07
* @Version 1.0
* @Describe 抽象元素角色
*/
public interface Animals {
void accept(Person person);
}
具体元素角色:
/**
* @Autor: wl
* @Date: 2021-09-02 22:10
* @Version 1.0
* @Describe 具体元素角色
*/
public class Cat implements Animals{
@Override
public void accept(Person person) {
person.feed(this);
System.out.println("真好吃,喵喵瞄");
}
}
/**
* @Autor: wl
* @Date: 2021-09-02 22:08
* @Version 1.0
* @Describe 具体元素角色
*/
public class Dog implements Animals {
@Override
public void accept(Person person) {
person.feed(this);
System.out.println("真好吃,汪汪汪");
}
}
抽象访问角色:
/**
* @Autor: wl
* @Date: 2021-09-02 22:08
* @Version 1.0
* @Describe 抽象访问角色
*/
public interface Person {
void feed(Dog dog);
void feed(Cat cat);
}
具体访问角色:
/**
* @Autor: wl
* @Date: 2021-09-02 22:11
* @Version 1.0
* @Describe
*/
public class Owner implements Person {
@Override
public void feed(Dog dog) {
System.out.println("主人喂狗");
}
@Override
public void feed(Cat cat) {
System.out.println("主人喂猫");
}
}
/**
* @Autor: wl
* @Date: 2021-09-02 22:12
* @Version 1.0
* @Describe
*/
public class Visitor implements Person {
@Override
public void feed(Dog dog) {
System.out.println("访客喂狗");
}
@Override
public void feed(Cat cat) {
System.out.println("访客喂猫");
}
}
测试类:
/**
* @Autor: wl
* @Date: 2021-09-02 22:15
* @Version 1.0
* @Describe
*/
public class Test {
public static void main(String[] args) {
Home home = new Home();
home.add(new Dog());
home.add(new Cat());
Owner owner = new Owner();
home.action(owner);
}
}
优点:
-
扩展性好,在不改变对象结构中的元素的情况下,为对象结构中的元素添加新的功能
-
复用性好,通过访问者来定义整个对象结构的通用功能,从而提高复用成程度
-
分离无关行为,通过访问者来分离无关的行为,把相关的行为封装在一起,构成一个访问者,这样每个访问者的功能都比较单一。
缺点:
-
对象结构变化很困难,在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”
-
违反了依赖倒置原则,访问者模式依赖了具体类,而没有依赖抽象类。
使用场景:
-
对象结构相对稳定,但其操作算法经常变化的程序
-
对象结构中的对象需要提供多种不同且不相关的操作,而且要避免让这些操作的变化影响对象的结构。
行为型设计模式-备忘录模式(黑箱)
备忘录模式的主要角色如下:
-
发起人(Originator)角色:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据
的功能,实现其他业务功能,它可以访问备忘录里的所有信息。 -
备忘录(Memento)角色:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起
人。 -
管理者(Caretaker)角色:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备
忘录的内容进行访问与修改。
备忘录角色对发起人对象提供一个宽接口,而为其他对象提供一个窄接口。在Java语言中,实现双重
接口的办法就是将备忘录类设计成发起人类的内部成员类。
类图如下:
窄接口:
/**
* @Autor: wl
* @Date: 2021-09-03 16:55
* @Version 1.0
* @Describe 窄接口
*/
public interface Memento {
}
发起人角色:
/**
* @Autor: wl
* @Date: 2021-09-03 16:50
* @Version 1.0
* @Describe 发起人角色
*/
public class GameRole {
// 生命力
private int health;
// 攻击力
private int destroy;
// 防御力
private int defence;
public GameRole(int health, int destroy, int defence) {
this.health = health;
this.destroy = destroy;
this.defence = defence;
}
public GameRole() {
}
// 初始化状态
public void initState(){
this.health=100;
this.defence=100;
this.destroy=100;
}
public void fight(){
this.health=0;
this.defence=0;
this.destroy=0;
}
// 保存角色状态
public Memento saveState(){
return new RoleStateMemento(health,destroy,defence);
}
// 恢复角色状态
public void recoverState(Memento memento){
RoleStateMemento roleStateMemento= (RoleStateMemento) memento;
this.health=roleStateMemento.getHealth();
this.destroy=roleStateMemento.getDestroy();
this.defence=roleStateMemento.getDefence();
}
// 展示角色状态信息
public void stateDisplay(){
System.out.println("攻击力:"+destroy);
System.out.println("防御力:"+defence);
System.out.println("生命力:"+health);
}
private class RoleStateMemento implements Memento{
// 生命力
private int health;
// 攻击力
private int destroy;
// 防御力
private int defence;
public RoleStateMemento() {
}
public RoleStateMemento(int health, int destroy, int defence) {
this.health = health;
this.destroy = destroy;
this.defence = defence;
}
public int getHealth() {
return health;
}
public void setHealth(int health) {
this.health = health;
}
public int getDestroy() {
return destroy;
}
public void setDestroy(int destroy) {
this.destroy = destroy;
}
public int getDefence() {
return defence;
}
public void setDefence(int defence) {
this.defence = defence;
}
}
}
管理者角色:
/**
* @Autor: wl
* @Date: 2021-09-03 17:04
* @Version 1.0 管理者角色
* @Describe
*/
public class RoleStateCaretaker {
private Memento memento;
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}
测试类
/**
* @Autor: wl
* @Date: 2021-09-03 17:05
* @Version 1.0
* @Describe 测试类
*/
public class Test {
public static void main(String[] args) {
System.out.println("=============大战boss前============");
GameRole gameRole = new GameRole();
gameRole.initState();
gameRole.stateDisplay();
// 保存进度
RoleStateCaretaker stateCaretaker = new RoleStateCaretaker();
stateCaretaker.setMemento(gameRole.saveState());
System.out.println("==============大战boss后=========");
// 大战boss后
gameRole.fight();
gameRole.stateDisplay();
System.out.println("==============恢复之前的状态=========");
// 恢复之前的状态
gameRole.recoverState(stateCaretaker.getMemento());
gameRole.stateDisplay();
}
}
优点:
-
提供了一种可以恢复状态的机制。当用户需要时能够比较方便地将数据恢复到某个历史的状态。
-
实现了内部状态的封装。除了创建它的发起人之外,其他对象都不能够访问这些状态信息。
缺点:
- 资源消耗大。如果要保存的内部状态信息过多或者特别频繁,将会占用比较大的内存资源
使用场景:
- 需要保存与恢复数据的场景,如玩游戏时的中间结果的存档功能。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)