常用的设计模式和使用案例汇总

常用的设计模式和使用案例汇总

  • 【一】常用的设计模式介绍
    • 【1】设计模式分类
    • 【2】软件设计七大原则(OOP原则)
  • 【二】单例模式
    • 【1】介绍
    • 【2】饿汉式单例
    • 【3】懒汉式单例
    • 【4】静态内部类单例
    • 【5】枚举(懒汉式)
  • 【三】工厂方法模式
    • 【1】简单工厂模式:
    • 【2】工厂方法模式
    • 【3】抽象工厂模式
    • 【4】抽象工程模式的使用案例
      • (1)需求描述
      • (2)实现逻辑
  • 【四】代理模式
    • 【1】静态代理模式
    • 【2】动态代理模式
  • 【五】装饰者模式
  • 【六】观察者模式
  • 【七】责任链模式
    • 【1】介绍
    • 【2】使用案例一
    • 【3】责任链模式解决登录过程层层校验问题
    • 【4】责任链模式+建造者模式结合使用优化
  • 【八】策略模式
    • 【1】策略模式的使用过程?
    • 【2】抽离出来的策略类如何管理?如何取消if-else的判断?
    • 【3】案例一:使用不同的支付方式
    • 【4】案例二:策略模式+工厂模式来解决请假情况判断问题
  • 【九】其他常用设计模式
  • 【十】设计模式的组合使用

【一】常用的设计模式介绍

常用的七种设计模式:单例模式、工厂方法模式、抽象工厂模式、代理模式、装饰器模式、观察者模式、责任链模式。

【1】设计模式分类

设计模式根据工作的目的,分为创建型模式、结构型模式和行为型模式三类。
(1)创建型模式:单例模式、工厂方法模式、抽象工厂模式、创建者模式、原型模式。
(2)结构型模式:适配器模式、代理模式、装饰器模式、外观模式、桥接模式、组合模式、享元模式。
(3)行为型模式:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

【2】软件设计七大原则(OOP原则)

(1)开闭原则:对扩展开放,对修改关闭。
(2)里氏替换原则:不要破坏继承体系,子类重写方法功能发生改变,不应该影响父类方法的含义。
(3)依赖倒置原则:要面向接口编程,不要面向实现编程。
(4)单一职责原则:控制类的粒度大小、将对象解耦、提高其内聚性。
(5)接口隔离原则:要为各个类建立它们需要的专用接口。
(6)迪米特法则:一个类应该保持对其它对象最少的了解,降低耦合度。
(7)合成复用原则:尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。

实际上,七大原则的目的只有一个:降低对象之间的耦合,增加程序的可复用性、可扩展性和可维护性。

【二】单例模式

【1】介绍

一个类只有一个实例,且该类能自行创建这个实例的一种模式
(1)单例类只有一个实例对象
(2)该单例对象必须由单例类自行创建
(3)单例类对外提供一个访问该单例的全局访问点
(4)优点
单例模式可以保证内存里只有一个实例,减少了内存的开销。
可以避免对资源的多重占用。
单例模式设置全局访问点,可以优化和共享资源的访问。
(5)缺点
单例模式一般没有接口,扩展困难。
单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则

【2】饿汉式单例

类一旦加载就创建一个单例,保证在调用getInstance方法之前单例已经存在,这种饿汉式单例会造成空间浪费。

public class Hungry {private Hungry(){}private final static Hungry HUNGRY = new Hungry();public static Hungry getInstance(){return HUNGRY;}
}

【3】懒汉式单例

为了避免内存空间浪费,采用懒汉式单例,即用到该单例对象的时候再创建。

public class LazyMan {private LazyMan(){};public static LazyMan lazyMan;public static LazyMan getInstance(){if (lazyMan==null){lazyMan = new LazyMan();}return lazyMan;}
}

这是最简单的懒汉式,但是,存在很大问题,单线程下这段代码没有问题,但是在多线程下有很大问题。

public class LazyMan {private LazyMan(){System.out.println(Thread.currentThread().getName()+"");}public static LazyMan lazyMan;public static LazyMan getInstance(){if (lazyMan==null){lazyMan = new LazyMan();}return lazyMan;}public static void main(String[] args) {for(int i=0;i<10;i++){new Thread(()->{lazyMan.getInstance();}).start();}}
}

在这里插入图片描述
会发现结果都不一样,因此,并发情况下,这段代码是有问题的。我们需要进行两端检测,进行“加锁”:synchronized (Singleton.class)。

public class LazyMan {private LazyMan(){System.out.println(Thread.currentThread().getName()+"");}public static LazyMan lazyMan;public static LazyMan getInstance(){if (lazyMan==null){        //第一层检查,检查是否有引用指向对象,高并发情况下会有多个线程同时进入synchronized (LazyMan.class){    //第一层锁,保证只有一个线程进入//双重检查,防止多个线程同时进入第一层检查(因单例模式只允许存在一个对象,故在创建对象之前无引用指向对象,所有线程均可进入第一层检查)//当某一线程获得锁创建一个LazyMan对象时,即已有引用指向对象,lazyMan不为空,从而保证只会创建一个对象//假设没有第二层检查,那么第一个线程创建完对象释放锁后,后面进入对象也会创建对象,会产生多个对象if(lazyMan==null){    //第二层检查//synchronized关键字作用为禁止指令重排,保证返回Singleton对象一定在创建对象后lazyMan = new LazyMan();        //这行代码存在的问题,不能保证原子性
实际上会执行以下内容://(1)在堆上开辟空间;(2)属性初始化;(3)引用指向对象//假设以上三个内容为三条单独指令,因指令重排可能会导致执行顺序为1->3->2(正常为1->2->3),当单例模式中存在普通变量需要在构造方法中进行初始化操作时,单线程情况下,顺序重排没有影响;但在多线程情况下,假如线程1执行lazyMan = new LazyMan()语句时先1再3,由于系统调度线程2的原因没来得及执行步骤2,但此时已有引用指向对象也就是lazyMan!=null,故线程2在第一次检查时不满足条件直接返回lazyMan,此时lazyMan为null//synchronized关键字可保证lazyMan = new LazyMan()语句执行顺序为123,因其为非原子性依旧可能存在系统调度问题(即执行步骤时被打断),但能确保的是只要lazyMan!=0,就表明一定执行了属性初始化操作;而若在步骤3之前被打断,此时lazyMan依旧为null,其他线程可进入第一层检查向下执行创建对象}}}return lazyMan;}public static void main(String[] args) {for(int i=0;i<10;i++){new Thread(()->{lazyMan.getInstance();}).start();}}
}

在这里插入图片描述可以看到结果都是只有一个,按理来说是没有问题,实际上不是,上述标注行代码存在问题的,不是一个原子性操作。

【4】静态内部类单例

是不安全,存在问题的,是不安全,存在问题的(修改过的懒汉式单例也是如此)。

public class Inner {private Inner(){}public static Inner getInstance(){return InnerClass.INNER;}public static class InnerClass{private static final Inner INNER = new Inner();}
}
class Singleton06{//1-构造器私有化private Singleton06(){ }//2-本类内部创建对象实例(加上了volatile)private static volatile Singleton06 instance;//3-写一个静态内部类,该类中有一个静态属性private static class SingletonInstance{private static final Singleton06 INSTANCE=new Singleton06();}//4-提供一个静态的公有方法,直接返回SingletonInstance.INSTANCEpublic static synchronized Singleton06 getInstance(){return SingletonInstance.INSTANCE;}
}

(1)使用静态内部类完成对象实例化,这种方式采用类装载的机制来保证初始化实例时只有一个线程
(2)静态内部类方式在Singleton06类被装载时并不会立即实例化,而是在需要实例化时,才调用getInstance方法
(3)类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化的时候,别的线程是无法进入的
(4)所以说:避免了线程不安全,利用静态内部类特点实现延迟加载,效率高,推荐使用

【5】枚举(懒汉式)

enum Singleton07{INSTANCE;public void sayOK(){System.out.println("ok-");}
}public class SingletonTest07 {public static void main(String[] args) {Singleton07 instance01=Singleton07.INSTANCE;Singleton07 instance02=Singleton07.INSTANCE;System.out.println(instance01==instance02);System.out.println("instance01-hashCode="+instance01.hashCode());System.out.println("instance02-hashCode="+instance02.hashCode());instance01.sayOK();}
}

【三】工厂方法模式

实例化对象不是用new,用工厂方法替代。将选择实现类,创建对象统一管理和控制。从而将调用者跟我们的实现类解耦。

以往博客
工厂模式

【1】简单工厂模式:

用来生产同一等级架构中的任意产品(对于增加新的产品,需要修改已有代码)
在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例。

接下来创建一个接口,两个实现类,一个工厂,一个测试类

(1)创建手机接口
(2)创建两个不同手机的实现类
(3)创建对象工厂,传参为手机品牌,工厂内部通过if判断品牌,返回对应品牌的手机对象
比较简单暴力,所以叫简单工厂模式

//创建手机接口
public interface Phone {void name();
}
//创建华为实现类
public class HuaWei implements Phone{@Overridepublic void name() {System.out.println("华为手机");}
}
//创建小米实现类
public class XiaoMi implements Phone{@Overridepublic void name() {System.out.println("小米手机");}
}
//创建工厂
public class PhoneFactory {public static Phone getPhone(String phone){if(phone.equals("华为")){return new HuaWei();}else if(phone.equals("小米")){return  new XiaoMi();}else {return null;}}
}
//测试类
public class Consumer {public static void main(String[] args) {Phone p1= PhoneFactory.getPhone("华为");Phone p2= PhoneFactory.getPhone("小米");p1.name();p2.name();}
}

我们通过创建一个PhoneFactory类,成功的完成工厂的创建。我们在创建对象时,也就不需要直接创建对象,而是可以通过创建工厂,这样大大的降低了代码的耦合性。但是,静态工厂模式是不能添加数据的。比如说,我们想添加一个“Oppo”手机类,你不直接修改PhoneFactory工厂代码,是不能实现的。所以,就有了第二种的工厂方法模式。

【2】工厂方法模式

用来生产同一等级架构中的固定产品,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建 。(支持增加任意产品)

//创建手机接口
public interface Phone {void name();
}
//创建华为实现类
public class HuaWei implements Phone{@Overridepublic void name() {System.out.println("华为手机");}
}
//创建手机工厂接口
public interface PhoneFactory {Phone getPhone();
}
//创建华为工厂
public class HuaWeiFactory implements PhoneFactory{@Overridepublic Phone getPhone() {return new HuaWei();}
}
//测试类
public class Consumer {public static void main(String[] args) {Phone phone = new HuaWeiFactory().getPhone();phone.name();}
}

我们创建了手机工厂接口PhoneFactory,再创建华为工厂HuaWeiFactory实现工厂,这样就可以通过HuaWeiFactory创建对象。增加新的具体工厂和产品族很方便,比如说,我们想要增加小米,只需要创建一个小米工厂XiaoMiFactory实现手机工厂接口PhoneFactory,合理的解决的简单工厂模式不能修改代码的缺点。但是,在现实使用中,简单工厂模式占绝大多数。

【3】抽象工厂模式

抽象工厂模式提供了一个创建一系列相关或者相互依赖对象的接口,无需指定它们具体的类(围绕一个超级工厂创建其他工厂,该超级工厂称为工厂的工厂)

抽象工厂模式得主要角色:
(1)抽象工厂(Abstract Factory)提供了创建产品的接口,它包含多个创建产品的方法 newProduct(),可以创建多个不同等级的产品。
(2)具体工厂(Concrete Factory)主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
(3)抽象产品(Product)定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
(4)具体产品(ConcreteProduct)实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。

//电脑接口
public interface Computer {void play();void watch();
}
//创建华为电脑对象
public class HuaWeiComputer implements Computer{@Overridepublic void play() {System.out.println("HuaWei's play!");}@Overridepublic void watch() {System.out.println("HuaWei's watch!");}
}
//手机接口
public interface Phone {void send();void call();
}
//创建华为手机对象
public class HuaWeiPhone implements Phone{@Overridepublic void send() {System.out.println("HuaWei's send");}@Overridepublic void call() {System.out.println("HuaWei's call");}
}
//抽象工厂
public interface IProductFactory {//生产手机Phone phone();//生产电脑Computer computer();
}
//创建华为工厂
public class HuaWeiFactory implements IProductFactory{@Overridepublic Phone phone() {return new HuaWeiPhone();}@Overridepublic Computer computer() {return new HuaWeiComputer();}
}
//测试类
public class Consumer {public static void main(String[] args) {HuaWeiFactory huaWeiFactory = new HuaWeiFactory();Phone phone = huaWeiFactory.phone();phone.call();phone.send();Computer computer = huaWeiFactory.computer();computer.play();computer.watch();}
}

我们通过创建一个抽象工厂完成了对具体工厂的创建,只需要传入参数就可以实例化对象。具体产品在应用层的代码隔离,无需关心创建的细节将一个系列的产品统一到一起创建。将一系列产品规划到一起创建。但是,抽象工厂模式也存在着缺点。规定了所有可能被创建的产品集合,产品簇中扩展新的产品困难,不可以增加产品,只能增加品牌。

【4】抽象工程模式的使用案例

(1)需求描述

公司早期介入七牛云OSS(对象存储服务)上传图片与视频,后因业务调整,巩乃斯要求额外支持阿里云、腾讯云等其他云服务商,并且可以对客户提供外界访问。

设计要求:允许在不破坏原有代码逻辑的情况下,实现对任意三方云厂商的接入。

(2)实现逻辑

(1)创建工厂的工厂

public interface AbstractOssFactory {public OssImage uploadImage(byte[] bytes);public OssVideo uploadVideo(byte[] bytes);
}public interface OssImage {public String getThumb();public String getWatermark();public String getEnhance();
}public interface OssVideo {public String get720p();public String get1080p();
}

(2)创建阿里云工厂的实现类

/*** @ClassName: AliyunOssFacotry* @Author: AllenSun* @Date: 2023/2/12 下午1:41*/
public class AliyunOssFacotry implements AbstractOssFactory {@Overridepublic OssImage uploadImage(byte[] bytes) {return new AliyunOssImage(bytes,"Allen",true);}@Overridepublic OssVideo uploadVideo(byte[] bytes) {return new AliyunOssVideo(bytes,"Allen");}
}/*** @ClassName: AliyunOssImage* @Author: AllenSun* @Date: 2023/2/12 下午1:43*/
public class AliyunOssImage implements OssImage {private byte[] bytes;public AliyunOssImage(byte[] bytes, String watermark, boolean transparent) {this.bytes = bytes;System.out.println("【阿里云】图片已上传至阿里云OSS");System.out.println("【阿里云】已生成缩略图,尺寸640*480像素");System.out.println("【阿里云】已为图片新增水印,水印文本:"+watermark+",文本透明度:"+transparent);System.out.println("【阿里云】已将图片AI增强为4K极清画质");}@Overridepublic String getThumb() {return "获取阿里云缩略图";}@Overridepublic String getWatermark() {return "获取阿里云水印";}@Overridepublic String getEnhance() {return "获取阿里云画质增强图";}
}/*** @ClassName: AliyunOssImage* @Author: AllenSun* @Date: 2023/2/12 下午1:43*/
public class AliyunOssVideo implements OssVideo {private byte[] bytes;public AliyunOssVideo(byte[] bytes, String watermark) {this.bytes = bytes;System.out.println("【阿里云】视频已上传至阿里云南OSS");System.out.println("【阿里云】720p转码成功,码率:5000K");System.out.println("【阿里云】1080p转码成功,码率:7000K");}@Overridepublic String get720p() {return "返回阿里云720p视频";}@Overridepublic String get1080p() {return "返回阿里云1080p视频";}
}

(3)创建七牛云工厂的实现类

/*** @ClassName: AliyunOssFacotry* @Author: AllenSun* @Date: 2023/2/12 下午1:41*/
public class QiniuyunOssFacotry implements AbstractOssFactory {@Overridepublic OssImage uploadImage(byte[] bytes) {return new QiniuyunOssImage(bytes,"Allen",true);}@Overridepublic OssVideo uploadVideo(byte[] bytes) {return new QiniuyunOssVideo(bytes,"Allen");}
}/*** @ClassName: AliyunOssImage* @Author: AllenSun* @Date: 2023/2/12 下午1:43*/
public class QiniuyunOssImage implements OssImage {private byte[] bytes;public QiniuyunOssImage(byte[] bytes, String watermark, boolean transparent) {this.bytes = bytes;System.out.println("【七牛云】图片已上传至阿里云OSS");System.out.println("【七牛云】已生成缩略图,尺寸640*480像素");System.out.println("【七牛云】已为图片新增水印,水印文本:"+watermark+",文本透明度:"+transparent);System.out.println("【七牛云】已将图片AI增强为4K极清画质");}@Overridepublic String getThumb() {return "获取七牛云缩略图";}@Overridepublic String getWatermark() {return "获取七牛云水印";}@Overridepublic String getEnhance() {return "获取七牛云画质增强图";}
}/*** @ClassName: AliyunOssImage* @Author: AllenSun* @Date: 2023/2/12 下午1:43*/
public class QiniuyunOssVideo implements OssVideo {private byte[] bytes;public QiniuyunOssVideo(byte[] bytes, String watermark) {this.bytes = bytes;System.out.println("【七牛云】视频已上传至阿里云南OSS");System.out.println("【七牛云】720p转码成功,码率:5000K");System.out.println("【阿里云】1080p转码成功,码率:7000K");}@Overridepublic String get720p() {return "返回七牛云720p视频";}@Overridepublic String get1080p() {return "返回七牛云1080p视频";}
}

(4)测试类

/*** @ClassName: Client* @Author: AllenSun* @Date: 2023/2/12 下午2:04*/
public class Client {public static void main(String[] args) {AbstractOssFactory factory = new AliyunOssFacotry();OssImage ossImage = factory.uploadImage(new byte[1024]);OssVideo ossVideo = factory.uploadVideo(new byte[1024]);System.out.println(ossImage.getThumb());System.out.println(ossImage.getWatermark());System.out.println(ossImage.getEnhance());System.out.println(ossVideo.get720p());System.out.println(ossVideo.get1080p());}
}

【四】代理模式

以往博客
代理模式

由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。

【1】静态代理模式

角色分析:
(1)抽象角色:一般会使用接口或抽象类来解决
(2)真实角色:被代理的角色
(3)代理角色:代理真实角色,代理真实角色后我们会进行一些附属操作
(4)访问角色:访问代理对象的人

//租房
public interface Rent {void rent();
}
//房东
public class Master implements Rent{@Overridepublic void rent() {System.out.println("Master rent");}
}
//中介
public class Proxy implements Rent{private Master master;public Proxy() {}public Proxy(Master master) {this.master = master;}@Overridepublic void rent() {see();master.rent();fare();}//看房public void see(){System.out.println("see");}//收费public void fare(){System.out.println("fare");}
}
//测试类
public class Consumer {public static void main(String[] args) {Master master = new Master();//进行代理Proxy proxy = new Proxy(master);//不需要通过对象,直接通过代理完成响应业务proxy.rent();}
}

得到测试结果:see——》Master rent——》fare

可以从上述代码看出,我们通过创建中介这一代理完成了租房,并且还有看房、收费的附属操作。我们不需要使用房东对象,通过使用代理中介就可以完成操作。
(1)代理模式优点:可以使真实角色的操作更加纯粹!不用去关注一些公共的业务,公共也就可以交给代理角色,实现了业务的分工,公共业务发生扩展的时候,方便集中管理!
(2)代理模式缺点:一个真实角色就会产生一个代理角色;代码量会翻倍开发效率会变低,也许,这样无法理解到代理模式的好处。举个例子也许能更好理解,比如说我们想要在原有固定功能上新增业务,按照开闭原则我们是不能对原有代码进行修改的。但是我们可以通过代理模式,增加代理,在实现原有功能的情况下写入新的功能,创建对象时也就可以使用代理,完成操作。

【2】动态代理模式

虽然静态代理模式可以很好的解决开闭原则,但是每有一个真实角色就会产生一个代理,代码量翻倍过于臃肿,开发效率较低。因此,我们就使用动态代理模式进行设计。

//接口
public interface IUserService {void add();void delete();void update();void query();}
//实现类
public class UserServiceImpl implements IUserService {@Overridepublic void add() {System.out.println("add");}@Overridepublic void delete() {System.out.println("delete");}@Overridepublic void update() {System.out.println("update");}@Overridepublic void query() {System.out.println("query");}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//自动生成动态代理类模板
public class ProxyInvocationHandler implements InvocationHandler {//被代理接口private Object target;public void setTarget(Object target) {this.target = target;}//得到代理类public Object getProxy() {return Proxy.newProxyInstance(getClass().getClassLoader(), target.getClass().getInterfaces(), this);}public void log(String s) {System.out.println("[debug]:" + s);}//得到代理类@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {log(method.getName());Object result = method.invoke(target, args);return result;}
}
//测试类
public class Consumer {public static void main(String[] args) {UserServiceImpl userService = new UserServiceImpl();ProxyInvocationHandler handler = new ProxyInvocationHandler();//设置代理对象handler.setTarget(userService);//生成代理类IUserService proxy = (IUserService)handler.getProxy();proxy.add();proxy.query();}
}

通过测试我们可以顺利的使用动态代理模式完成一系列操作,当我们想要添加附属操作时,我们只需要在模板中进行添加。优点:
(1)可以使真实角色的操作更加纯粹!不用去关注一些公共的业务。
(2)公共也就可以交给代理角色!实现了业务的分工。
(3)公共业务发生扩展的时候,方便集中管理。
(4)一个动态代理类代理的是一个接口,一般就是对应的一类业务。
(5)一个动态代理类可以代理多个类,只要是实现了同一个接口即可!

【五】装饰者模式

以往博客
装饰者模式

动态的将新功能附加到对象上。在对象功能的拓展方面,比继承更有弹性。同时装饰者模式也体现了开闭原则。

角色分析:
(1)抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。
(2)具体构件(ConcreteComponent)角色:实现抽象构件,通过装饰角色为其添加一些职责。
(3)抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
(4)具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。

//定义抽象类
public abstract class Drink {public abstract double cost();
}
//定义两个抽象类的实现类
public class Juice extends Drink{@Overridepublic double cost() {System.out.println("juice: "+16);return 16;}
}
public class Milk extends Drink{@Overridepublic double cost() {System.out.println("milk: "+12);return 12;}
}
//定义装饰抽象类
public abstract class Decorator extends Drink {public abstract double cost();
}
//定义两个装饰具体实现类
public class Chocolate extends Decorator{private final static double COST = 4;private Drink drink;public Chocolate(Drink drink) {this.drink = drink;}@Overridepublic double cost() {System.out.println("chocolate:"+4);return COST+drink.cost();}
}
public class Pudding extends Decorator{private final static double COST = 5;private Drink drink;public Pudding(Drink drink) {this.drink = drink;}@Overridepublic double cost() {System.out.println("pudding:"+5);return COST+drink.cost();}
}
//测试类
public class Test {public static void main(String[] args) {Drink milk = new Milk();milk = new Pudding(milk);milk = new Chocolate(milk);System.out.println(milk.cost());}
}

得到测试结果
chocolate:4
pudding:5
milk: 12
21.0

可以看到非常简单的就能够完成具体构件和具体装饰的组合。也可以看到结构也非常简洁明,具体构件在自己的package中,具体装饰也在自己的package中。我们不管是想要增加具体构件还是具体配饰,都可以在各自的package中添加。对已有的代码不需要进行任何操作,就算新加的有bug也不会影响原有代码的操作。

【六】观察者模式

以往博客
观察者模式

对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。

观察者模式优点:
(1)降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。符合依赖倒置原则。
(2)目标与观察者之间建立了一套触发机制。

//定义观察者接口
public interface Observer {void response();
}
//具体化观察者1
public class Observer1 implements Observer{@Overridepublic void response() {System.out.println("Observer1 action");}
}
//具体化观察者2
public class Observer2 implements Observer{@Overridepublic void response() {System.out.println("Observer2 action");}
}
//抽象目标
public abstract class Subject {//创建观察者集合protected ArrayList<Observer> array = new ArrayList<Observer>();//增加观察者public void add(Observer observer){array.add(observer);}//删除观察者public void remove(Observer observer){array.remove(observer);}//通知观察者方法public abstract void notifyObserver();
}
//具体化目标
public class Subject1 extends Subject{@Overridepublic void notifyObserver() {for (Observer observer :array){observer.response();}}
}
//测试类
public class Test {public static void main(String[] args) {Subject subject = new Subject1();Observer obs1 = new Observer1();Observer obs2 = new Observer2();subject.add(obs1);subject.add(obs2);subject.notifyObserver();}
}

通过测试我们可以看到,我们不需要建立太多观察者和具体目标之间的联系,大大降低了目标与观察者之间的耦合关系。并且结构也十分简单明了,观察者和目标分别在各自的package包下。当我们想要添加观察者时,只需要在观察者包下进行添加,实现Observer接口就可以了。

【七】责任链模式

以往博客
责任链模式

【1】介绍

一种处理请求的模式,它让多个处理器都有机会处理该诘求,直到其中某个处理成功为止。责任链模式把多个处理器串成链,然后让请求在链上传递。

责任链模式的主要角色
(1)抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
(2)具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
(3)客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。

责任链模式优点
(1)降低了对象之间的耦合度。处理者不需要知道客户的任何信息,客户也不要知道处理者是如何实现方法的。
(2)提高了系统的灵活性。当我们想要新增处理器到整个链条中时,所付出的代价是非常小的

责任链模式缺点
(1)降低了系统的性能。对比较长的职责链,请求的处理可能涉及多个处理对象
不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求(2)可能一直传到链的末端都得不到处理。

【2】使用案例一

//抽象处理者
public abstract class Handler {private Handler next;public void setNext(Handler next) { this.next = next; }public Handler getNext() { return next; }//处理请求public abstract void handleRequest(int info);
}
//具体处理者1
public class Handler1 extends Handler{@Overridepublic void handleRequest(int info) {if (info <10){System.out.println("Handler1完成处理");}else {if (getNext()!=null){getNext().handleRequest(info);}else {System.out.println("没有处理者进行处理");}}}
}
//具体处理者2
public class Handler2 extends Handler{@Overridepublic void handleRequest(int info) {if (info <20&&info>10){System.out.println("Handler2完成处理");}else {if (getNext()!=null){getNext().handleRequest(info);}else {System.out.println("没有处理者进行处理");}}}
}
测试类
public class Test {public static void main(String[] args) {Handler handler1 = new Handler1();Handler handler2 = new Handler2();handler1.setNext(handler2);handler1.handleRequest(5);handler1.handleRequest(15);handler1.handleRequest(25);}
}

得到测试结果:

Handler1完成处理
Handler2完成处理
没有处理者进行处理

通过测试结果我们看到,5交给了Handler1处理,15交给了Handler2处理,而25则没有处理者处理。请求者根本不需要参与处理,只需要提交数据就可以完成功能的处理,完全不需要管是哪个处理者进行处理的。当我们想要继续添加处理者时,这只需要再次添加就可以了,也不会对之前的代码造成影响。

【3】责任链模式解决登录过程层层校验问题

(1)传统方式的登录校验代码

public class MemberService {public static void main(String[] args) {MemberService service = new MemberService();service.login("tom", "666");}public void login(String loginName, String loginPass) {if (StringUtils.isEmpty(loginName) ||StringUtils.isEmpty(loginPass)) {System.out.println("用户名和密码为空");return;}System.out.println("用户名和密码不为空,可以往下执行");Member member = checkExists(loginName, loginPass);if (null == member) {System.out.println("用户不存在");return;}System.out.println("登录成功!");if (!"管理员".equals(member.getRoleName())) {System.out.println("您不是管理员,没有操作权限");return;}System.out.println("允许操作");}private Member checkExists(String loginName, String loginPass) {Member member = new Member(loginName, loginPass);member.setRoleName("管理员");return member;}
}

(2)创建抽象的处理类Handler

public abstract class Handler {protected Handler next;public void next(Handler next){ this.next = next;}public abstract void doHandler(Member member);
}

(3)创建扩展了处理类的实体类
我们分别创建非空校验ValidateHandler类、登录校验LoginHandler类和权限校验AuthHandler类

public class ValidateHandler extends Handler {public void doHandler(Member member) {if (StringUtils.isEmpty(member.getLoginName()) ||StringUtils.isEmpty(member.getLoginPass())) {System.out.println("用户名和密码为空");return;}System.out.println("用户名和密码不为空,可以往下执行");next.doHandler(member);}
}public class LoginHandler extends Handler {public void doHandler(Member member) {System.out.println("登录成功!");member.setRoleName("管理员");next.doHandler(member);}
}public class AuthHandler extends Handler {public void doHandler(Member member) {if (!"管理员".equals(member.getRoleName())) {System.out.println("您不是管理员,没有操作权限");return;}System.out.println("允许操作");}
}

(4)在服务层设置链

public class MemberService {public void login(String loginName, String loginPass) {Handler validateHandler = new ValidateHandler();Handler loginHandler = new LoginHandler();Handler authHandler = new AuthHandler();validateHandler.next(loginHandler);loginHandler.next(authHandler);validateHandler.doHandler(new Member(loginName, loginPass));}
}

(5)测试代码

public class Test {public static void main(String[] args) {MemberService memberService = new MemberService();memberService.login("tom","666");}
}

【4】责任链模式+建造者模式结合使用优化

因为责任链模式具备链式结构,而上面代码中,我们看到,负责组装链式结构的角色是
MemberService ,当链式结构较长时,MemberService的工作会非常繁琐,并且MemberService代码相对臃肿,且后续更改处理者或消息类型时,都必须在MemberService中进行修改,,不符合开闭原则。产生这些问题的原因就是因为链式结构的组装过于复杂,而对于复杂结构的创建,很自然我们就会想到建造者模式,使用建造者模式,我们完全可以MemberService指定的处理节点对象进行自动链式组装,客户只需指定处理节点对象,其他任何事情都无需关心,并且客户指定的处理节点对象顺序不同 ,构造出来的链式结构也随之不同。我们来改造一下,修改Handler的代码:

public abstract class Handler<T> {protected Handler next;//public void next(Handler next){ this.next = next;}private void next(Handler next){ this.next = next;}public abstract void doHandler(Member member);public static class Builder<T>{private Handler<T> head;private Handler<T> tail;public Builder<T> addHandler(Handler handler){// do {if (this.head == null) {this.head = this.tail = handler;return this;}this.tail.next(handler);this.tail = handler;// }while (false);//真正框架中,如果是双向链表,会判断是否已经到了尾部return this;}public Handler<T> build(){return this.head;}}
}

然后 ,修改MemberService的代码:

public class MemberService {public void login(String loginName,String loginPass){Handler.Builder builder = new Handler.Builder();builder.addHandler(new ValidateHandler()).addHandler(new LoginHandler()).addHandler(new AuthHandler());builder.build().doHandler(new Member(loginName,loginPass));//用过Netty的人,肯定见过}
}

【八】策略模式

以往博客
策略模式

【1】策略模式的使用过程?

1-定义策略的接口interface,将策略抽象化提取出来
2-定义各种策略类,实现策略接口,在springboot中给每个策略类加上@Service(“WechatPay”)注解,将策略类放入IOC容器管理
3-使用策略的时候,直接根据参数PaymentType,到IOC容器中找到对应的策略类Bean对象,然后调用对应策略类的方法。

IPayment payment = applicationContext.getBean(order.getPaymentType(), IPayment.class);

参数是WechatPay,会到IOC容器中获取名称为WechatPay的Bean对象,也就是有注解@Service(“WechatPay”)的策略类,实例化对象后就可以调用对应的方法了。

【2】抽离出来的策略类如何管理?如何取消if-else的判断?

(1)如果是springboot,可以给策略类加上注解交给IOC容器管理,可以根据bean的名称直接获取到对应的类,也就不用if-else判断了
(2)还可以使用工厂模式

【3】案例一:使用不同的支付方式

public interface IPayment {/*** 支付* @param order* @return*/PayResult pay(Order order);
}@Service("WechatPay")
public class WechatPay implements IPayment {@Overridepublic PayResult pay(Order order) {return new PayResult("微信支付成功");}}@Service("Alipay")
public class Alipay implements IPayment {@Overridepublic PayResult pay(Order order) {return new PayResult("支付宝支付成功");}
}@Service("UnionPay")
public class UnionPay implements IPayment {@Overridepublic PayResult pay(Order order) {return new PayResult("云闪付支付成功");}
}@RestController
public class PayService {@Autowiredprivate ApplicationContext applicationContext;/*** 支付接口* @param amount* @param paymentType* @return*/@RequestMapping("/pay")public PayResult pay(@RequestParam("amount") int amount,@RequestParam("paymentType") String paymentType) {Order order = new Order();order.setAmount(amount);order.setPaymentType(paymentType);// 根据支付类型获取对应的策略 beanIPayment payment = applicationContext.getBean(order.getPaymentType(), IPayment.class);// 开始支付PayResult payResult = payment.pay(order);return payResult;}}

【4】案例二:策略模式+工厂模式来解决请假情况判断问题

看看用if-else怎么处理

/*** @ClassName: LeaveServiceOld* @Author: AllenSun* @Date: 2023/2/11 下午9:40*/
public class LeaveServiceOld {public void audit(LeaveForm leaveForm) {if(leaveForm.getDays()<=3 && leaveForm.getType()==1){System.out.println("三天以下婚丧假无需审批,自动通过");}if(leaveForm.getDays()>3 && leaveForm.getType()==1){System.out.println("三天以上婚丧假,进入上级审批流程");}if(leaveForm.getEmployee().getLevel()==9){System.out.println("总经理请假无需审批自动通过");}if(leaveForm.getDays()==1 && leaveForm.getType()==0){System.out.println("一天病假无需审批自动通过");}if(leaveForm.getDays()>1 && leaveForm.getType()==0){System.out.println("一天以上病假,进入上级审批流程");}}
}

开始使用策略模式进行改写,定义策略接口

public interface AuditStrategy {// 判断条件是否匹配public boolean isSupport(LeaveForm form);// 审核业务逻辑public void  audit(LeaveForm leaveForm);// 规则冲突时的优先级public int getPriority();// 规则名称public String getName();
}/** 三天以下婚丧假审批规则* @ClassName: AuditStrategyImpl_01* @Author: AllenSun* @Date: 2023/2/11 下午10:16*/
public class AuditStrategyImpl_01 implements AuditStrategy {@Overridepublic boolean isSupport(LeaveForm form) {return form.getDays()<=3 && form.getType()==1;}@Overridepublic void audit(LeaveForm leaveForm) {System.out.println(leaveForm);System.out.println("三天以下婚丧假无需审批,自动通过");}@Overridepublic int getPriority() {return 0;}@Overridepublic String getName() {return "三天以下婚丧假审批规则";}
}/** 三天以上婚丧假审批规则* @ClassName: AuditStrategyImpl_01* @Author: AllenSun* @Date: 2023/2/11 下午10:16*/
public class AuditStrategyImpl_02 implements AuditStrategy {@Overridepublic boolean isSupport(LeaveForm form) {return form.getDays()>3 && form.getType()==1;}@Overridepublic void audit(LeaveForm leaveForm) {System.out.println(leaveForm);System.out.println("三天以上婚丧假,进入上级审批流程");}@Overridepublic int getPriority() {return 0;}@Overridepublic String getName() {return "三天以上婚丧假审批规则";}
}

开始去除if-else,使用工厂模式,创建单例工厂

/*** @ClassName: AuditStrategyFactory* @Author: AllenSun* @Date: 2023/2/11 下午10:25*/
public class AuditStrategyFactory {// 单例工厂private final static AuditStrategyFactory FACTORY = new AuditStrategyFactory();// 创建一个list存放所有的策略类private List<AuditStrategy> auditStrategyList = new ArrayList<>();// 在构造器初始化里将所有策略类放入list中存放private AuditStrategyFactory() {auditStrategyList.add(new AuditStrategyImpl_01());auditStrategyList.add(new AuditStrategyImpl_02());auditStrategyList.add(new AuditStrategyImpl_03());auditStrategyList.add(new AuditStrategyImpl_04());auditStrategyList.add(new AuditStrategyImpl_05());}// 暴露一个方法,供外界获取单例工厂public static AuditStrategyFactory getInstance(){return FACTORY;};// 根据请假单的信息,返回对应的策略方法public AuditStrategy getAuditStrategy(LeaveForm leaveForm) {AuditStrategy auditStrategy = null;// 遍历list里的所有策略类,判断条件是否匹配for (AuditStrategy strategy : auditStrategyList) {// 如果策略的条件跟请假单的条件相符if(strategy.isSupport(leaveForm)){if(auditStrategy==null){// 获得目标策略类auditStrategy=strategy;} else {// 判断策略的优先级if(strategy.getPriority()==auditStrategy.getPriority()){// 如果两个策略类的优先级相同,则抛出异常throw new RuntimeException("["+auditStrategy.getName()+"]:["+strategy.getName()+"]规则冲突,请尽快解决。");} else if(strategy.getPriority()>auditStrategy.getPriority()){auditStrategy=strategy;} else {//do nothing}}}}if(auditStrategy==null){throw new RuntimeException("没有匹配的请假审核规则");} else {// 返回最终获取到的策略类return auditStrategy;}}
}

【九】其他常用设计模式

(1)模板模式
模板模式

(2)组合模式
组合模式

(3)命令模式
命令模式

(4)适配器模式

适配器模式

【十】设计模式的组合使用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/43950.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GuLi商城-商品服务-API-品牌管理-OSS获取服务端签名

新建第三方服务: 引入common 把common中oss的依赖都拿到第三方服务中来 配置文件: 加上nacos注解:<

HTML 标签简写和全称及其对应的中文说明和实例

<!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>HTML 标签简写及全称</title><style>…

Android 通知访问权限

问题背景 客户反馈手机扫描三方运动手表&#xff0c;下载app安装后&#xff0c;通知访问权限打不开。 点击提示“受限设置” “出于安全考虑&#xff0c;此设置目前不可用”。 问题分析 1、setting界面搜“授予通知访问权限”&#xff0c;此按钮灰色不可点击&#xff0c;点…

大小端详解

引例 我们知道整形(int)是4个字节&#xff0c;例如随便举个例子&#xff1a;0x01020304&#xff0c;它一共占了四个地址位&#xff0c;01,02,03,04分别占了一个字节&#xff08;一个字节就对应了一个地址&#xff09;。 那么就会有个问题&#xff1a;我们的01到底是存储在高地…

mysql 5.7.44 32位 zip安装

前言 因为研究别人代码&#xff0c;他使用了5.7的 32位 mysql &#xff0c;同时最新的 8.4 64位 mysql 不能用官方lib连接。所以安装这个版本使用&#xff0c;期间有些坑&#xff0c;在这里记录一下。 下载路径 mysql官方路径&#xff1a;https://downloads.mysql.com/archi…

Linux——多线程(五)

1.线程池 1.1初期框架 thread.hpp #include<iostream> #include <string> #include <unistd.h> #include <functional> #include <pthread.h>namespace ThreadModule {using func_t std::function<void()>;class Thread{public:void E…

Redis 7.x 系列【21】主从复制

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Redis 版本 7.2.5 源码地址&#xff1a;https://gitee.com/pearl-organization/study-redis-demo 文章目录 1. 概述2. 工作原理2.1 建立连接2.2 全量复制2.3 命令传播2.4 增量复制 3. 拓扑架构3.…

Uniapp表单提交

template中&#xff1a; <template><view class""><button class"tianjia" click"tianjia">添加</button><view class"divOne" v-show"a"><text class"guanbi" click"gua…

本地 HTTP 文件服务器的简单搭建 (deno/std)

首发日期 2024-06-30, 以下为原文内容: 在本地局域网搭建一个文件服务器, 有很多种方式. 本文介绍的是窝觉得比较简单的一种. 文件直接存储在 btrfs 文件系统之中, 底层使用 LVM 管理磁盘, 方便扩容. 使用 btrfs RAID 1 进行镜像备份 (一个文件在 2 块硬盘分别存储一份), 防止…

网络通信、BIO、NIO

1. 涉及的网络基础知识 Socket&#xff1a; 操作系统提供的api&#xff0c;介于应用层和tcp/ip层之间的软件层&#xff0c;封装服务器客户端之间网络通信相关内容&#xff0c;方便调用 IO多路复用&#xff1a; &#xff08;I/O Multiplexing&#xff09;是一种IO操作模式&a…

Python 的 metaclass

文章目录 先说结论1. metaclass 的作用2. 主要的执行过程 1. metaclass.__new__2. metaclass.__call__关于 metaclass.__init__ 3. metaclass.__prepare__4. 自动创建 __slots__ 属性4.1 metaclass 的接口类4.2 metaclass conflict 5. Class metaprogramming 先说结论 1. meta…

Java技术栈总结:JVM虚拟机篇

一、Java的四种引用类型 1、强引用 最常见的引用&#xff0c;类似Object obj new Object()、String str “hello”。如果一个对象具有强引用&#xff0c;垃圾回收器绝对不会回收它。即使抛出“OutOfMemoryError”错误&#xff0c;程序终止&#xff0c;也不会随意回收具有强引…

20240710 每日AI必读资讯

&#x1f916;微软&#xff1a;不会像 OpenAI 一样阻止中国访问 AI 模型 - OpenAI 将于周二&#xff08;7 月 9 日&#xff09;开始阻止中国用户访问其 API。 - 微软发言人表示&#xff1a;Azure OpenAI API服务在中国的提供方式没有变化。 - 公司仍然通过部署在中国以外地区…

妙笔生词智能写歌词软件:创新助力还是艺术之殇?

在音乐创作日益普及和多样化的当下&#xff0c;各种辅助工具层出不穷&#xff0c;妙笔生词智能写歌词软件便是其中之一。那么&#xff0c;它到底表现如何呢&#xff1f; 妙笔生词智能写歌词软件&#xff08;veve522&#xff09;的突出优点在于其便捷性和高效性。对于那些灵感稍…

c/c++:牛客小白月赛93

比赛链接 A 生不逢七 题目描述(题目链接添加链接描述)&#xff1a; 睡前游戏中最简单又最好玩的游戏就是这个啦&#xff01; 该游戏规则为&#xff1a;多名玩家轮流报数&#xff0c;当要报的数字中含有 7 或者是 7 的倍数时&#xff08;例如 37&#xff0c;49&#xff09;&…

腾讯又一平台即将停止运营

随着腾讯公司业务和战略的调整&#xff0c;某些业务逐渐退出历史舞台&#xff0c;如“腾讯直播平台NOW”&#xff0c;以及“QQ签到”&#xff0c;“腾讯待办”&#xff0c;“企鹅FM音频平台”等&#xff0c;最近又有一则重磅消息&#xff0c;那就是“腾讯课堂”也即将停止运营。…

类似评论、省市区这种具有层次结构的数据表怎么设计?

业务功能模块 评论、回复模块省市区表 设置一个给每个数据设置一个parent_id 例如&#xff1a; 某个视频下a写了条评论&#xff0c;那a的parent_id就是0;b回复了a&#xff0c;那b的parent_id就是a的id;c回复了b&#xff0c;那c的parent_id就是b的id; 这样&#xff0c;所有评论…

Mosh|初学者 SQL 教程

sql文件链接&#xff1a;链接: https://pan.baidu.com/s/1okjsgssdxMkfKf8FEos7DA?pwdf9a9 提取码: f9a9 在mysql workbench 导入 create_databases.sql 文件&#xff0c;下面是运行成功的界面 快捷方式&#xff1a;全部运行可以同时按下controlcommandenter &#xff0c;或者…

ceph存储

1 存储简介 存储的三种方式包括&#xff1a;块存储、文件存储、对象存储1。此外&#xff0c;还有内存存储、硬盘存储和闪存存储2。 内存存储&#xff1a;临时性数据存储方式&#xff0c;存储速度快&#xff0c;容量有限&#xff0c;通常用来存储正在使用的程序和数据。硬盘存…

【通信协议】八、CDL(Caterpillar Data Link)协议解析

1、协议简介 CDL(Caterpillar Data Link)是caterpillar的通信协议&#xff0c;该品牌发动机ECM与各控制单元进行通信时&#xff0c;采用基于RS-485的物理层规范进行开发的CDL协议进行通信&#xff1b; 2、物理层 信号传输方式&#xff1a;差分信号&#xff08;通过两条线的电…