第一篇:设计模式

目录

一. 设计模式分类

二. 创建型模式

1. 简单工厂

2. 工厂方法

3. 代理模式

4. 模板方法模式

5. 策略模式

6. 单例模式


一. 设计模式分类

1. 创建型模式

提供创建对象的机制。包括:简单工厂、工厂方法、抽象工厂、原型、单例、生成器。

2. 结构性模式

把对象和类组装成较大的结构。包括:装饰器、代理、组合、适配器、外观、桥接、享元。

3. 行为模式

负责对象间的高效沟通和责任委派。包括:模板方法、策略、责任链、迭代器、观察者、访问者、中介者、命令、状态、备忘录。

二. 创建型模式

1. 简单工厂

使用场景:当我们在不同情况下需要创建不同的实例时,就可以用工厂模式。

使用方式:子类实现一个公共接口,在工厂中根据条件判断返回不同的子类,统一的返回值是那个公共接口。

/**
 * 每个保司都需要保费测算,因此构建一个保费测算工厂
 */
public class CaculateFactory {
    /**
     * 根据保司名称获取对应的测算类
     */
    public static IInsuranceCaculate getCaculateService(String companyName){
        if(StringUtils.isEmpty(companyName)){
            return null;
        }
        if("taiPingYang".equals(companyName)){
            return new TaiPingYangCaculateService();
        }else if("zhongAn".equals(companyName)){
            return new ZhongAnCaculateService();
        }else if("PingAn".equals(companyName)){
            return new PingAnCaculateService();
        }
        return null;
    }
}

/**
 * 保费测算接口
 */
public interface IInsuranceCaculate {
    /**
     * 根据产品id计算保费
     * @param productId
     */
    BigDecimal caculatePremium(Long productId);
}

/**
 * 太平洋保费测算
 */
public class TaiPingYangCaculateService implements IInsuranceCaculate {
    @Override
    public BigDecimal caculatePremium(Long productId) {
        return new BigDecimal("100");
    }
}

/**
 * 众安保费测算
 */
public class ZhongAnCaculateService implements IInsuranceCaculate {
    @Override
    public BigDecimal caculatePremium(Long productId) {
        return new BigDecimal("200");
    }
}

/**
 * 平安保费测算
 */
public class PingAnCaculateService implements IInsuranceCaculate {
    @Override
    public BigDecimal caculatePremium(Long productId) {
        return new BigDecimal("300");
    }
}

/**
 * 使用
 */
public class CaculateFactoryDemo {
    public static void main(String[] args) {
        BigDecimal premium = CaculateFactory.getCaculateService("taiPingYang").caculatePremium(1L);
        System.out.println("该保司保费为:" + premium);
    }
}

2. 工厂方法

工厂方法是有一个工厂接口,各个保司测算分别创建工厂实现工厂接口,然后再各个工厂中创建测算类。

简单工厂和工厂方法的区别:

简单工厂是由工厂判断应该创建哪个实例,工厂方法是由客户端判断创建哪个实现类的工厂。

3. 代理模式

应用场景:想要在不改变原始代码的基础上进行功能增强,就可以使用代理模式

项目中使用:比如想打印每个请求的入参出参,但是不想改动现有代码,就可以使用代理代理。项目中一般使用aop,aop的底层原理就是动态代理。

//目的:创建Image的代理

//image接口
public interface Image {
   void display();
}

//image实现类
public class RealImage implements Image {
   @Override
   public void display() {
      System.out.println("Displaying " + fileName);
   }
}

//image代理
public class ProxyImage implements Image{
   private RealImage realImage;

   @Override
   public void display() {
      if(realImage == null){
         realImage = new RealImage(fileName);
      }
      realImage.display();
   }
}

//使用
public class Test {
   public static void main(String[] args) {
      Image image = new ProxyImage("test_10mb.jpg");
      image.display(); 
   }
}
  • AspectJ就是静态代理,静态代理是在编译时期就把通知织入字节码文件中,运行的直接是增强后的字节码对象。
  • SpringAOP是动态代理,是在运行时动态的生成一个AOP对象,这个对象包含了被代理对象的全部方法,并且在特定切点做了增强处理,并回调原对象的方法。

动态代理

使用动态代理是为了给目标方法添加预处理或者后续操作,而不影响改变目标方法。

动态代理分为两种:基于JDK的动态代理、基于CGLib的动态代理。

基于JDK的动态代理

基于JDK的动态代理要主要是一个接口和一个类:InvocationHandler接口 和 Proxy类

//接口
public interface Subject {
    void hello(String param);
}

//实现类
public class SubjectImpl implements Subject {
    @Override
    public void hello(String param) {
        System.out.println("hello  " + param);
    }
}

//创建代理类
public class SubjectProxy implements InvocationHandler {
    private Subject subject;

    public SubjectProxy(Subject subject) {
        this.subject = subject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("--------------begin-------------");
        Object invoke = method.invoke(subject, args);
        System.out.println("--------------end-------------");
        return invoke;
    }
}

//使用
public class Main {
    public static void main(String[] args) {
        Subject subject = new SubjectImpl();
        InvocationHandler subjectProxy = new SubjectProxy(subject);
        Subject proxyInstance = (Subject) Proxy.newProxyInstance(subjectProxy.getClass().getClassLoader(), subject.getClass().getInterfaces(), subjectProxy);
        proxyInstance.hello("world");
    }
}

基于CGLib的动态代理

CGLib主要使用MethodInterceptor接口和Enhance类来创建代理类

//目标类
public class CGsubject {
    public void sayHello(){
        System.out.println("hello world");
    }
}

//代理类
public class HelloInterceptor implements MethodInterceptor{
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("begin time -----> "+ System.currentTimeMillis());
        Object o1 = methodProxy.invokeSuper(o, objects);
        System.out.println("end time -----> "+ System.currentTimeMillis());
        return o1;
    }
}

//使用
public class Main {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(CGsubject.class);
        enhancer.setCallback(new HelloInterceptor());
        CGsubject cGsubject = (CGsubject) enhancer.create();
        cGsubject.sayHello();
    }
}

JDK动态代理和CGLib动态代理的区别是什么?

JDK动态代理只能代理接口,如果没有接口就可以使用CGLib动态代理,CGLib动态代理可以代理类,当类被final修饰的时候就用不了CGLib了。

JDK动态代理的原理是用反射的方式增强了被代理的接口,CGLib代理相当于重写了父类的方法。

4. 模板方法模式

应用场景:一个算法的基本步骤一样,但是对几个具体事件的处理方式不同,就可以把公共的方法抽成模板方法。父类定义骨架(调用哪些方法及顺序),特定方法由子类实现。

项目中使用:比如业管系统上传出单excel的时候,每个excel模板的处理逻辑不同,但是对excel的处理方式是固定的:解析excel、校验数据、存储数据。此时就可以抽象成模板方法,不同实现类实现接口。

5. 策略模式

详细讲解:策略设计模式使用? - 知乎

使用场景:用户不同的行为使用不同的策略,这时候可以把策略封装起来,想用哪个策略的时候随时切换。

项目中使用:比如财富平台要对产品进行排序,可以按利率排序,也可以按持有时间排序,就可以使用策略模式(再比如高德给用户规划路线,从A地到B地,用户可以选择步行、自行车、开车,就可以使用策略模式)

/**
 * 策略接口
 */
public interface SortService{
    public List<Product> sortProduct(List<Product> list);
}

/**
 * 按利率排序策略实现类
 */
public class SortByRateService implements SortService{
    @Override
    public List<Product> sortProduct(List<Product> list) {
        System.out.println("按利率排序");
        return null;
    }
}

/**
 * 按默认排序策略实现类
 */
public class SortByDefaultService implements SortService{
    @Override
    public List<Product> sortProduct(List<Product> list) {
        System.out.println("默认排序");
        return null;
    }
}

/**
 * 产品类,除了排序可能还有别的功能
 */
public class ProductService{
    SortService sortService;

    //通过构造函数传入策略
    public ProductService(SortService sortService){
        this.sortService = sortService;
    }

    //使用时调用该方法排序
    public List<Product> sortProduct(List<Product> list) {
        return sortService.sortProduct();
    }
}

/**
 * 使用
 */
public class Test{
    public static void main(String[] args) {
        ProductService productService = new ProductService(new SortByRateService());
        productService.sortProduct(new ArrayList<Product>());
    }
}
Person per1 = new Person(new 自行车);
per1.action();
Person per2 = new Person(new 汽车);
per2.action();

6. 单例模式

// 饿汉模式
public final class Singleton {
    private static Singleton instance=new Singleton();// 自行创建实例
    private Singleton(){}// 构造函数
    public static Singleton getInstance(){// 通过该函数向整个系统提供实例
        return instance;
    }
}
// 懒汉模式
public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
        if (singleton == null) {  
            synchronized (Singleton.class) {  
                if (singleton == null) {  
                    singleton = new Singleton();  
                }      
        }  
    }  
    return singleton;  
    }  
}

为什么要进行双重判断?

        第一重判断是为了不要每次进来都同步,第二重判断是为了因为第一重判断不在同步范围内,可能此时已经被赋值了。

手写单例模式和Spring中的单例有什么区别?

        普通单例模式指的是在JVM中只有一个bean的实例,但是Spring中指的是一个Spring容器中只有一个bean的实例,如果有多个Spring容器,就有多个bean实例。(其实就是判断Spring容器中是否有该bean的实例)

7. 适配器模式

       适配器模式类似于转接头,可以将一个类的接口转成用户期望的另一个接口

        应用场景:比如自己有一个标准的支付接口,第三方提供了也提供了支付接口,但和标准接口不适配,就可以创建第三方接口的适配器来适配自己的标准支付接口,从而能和整个系统集成

// 标准支付接口
interface PaymentService {
    void pay(double amount);
}

// 第三方支付服务的接口(不兼容)
interface ThirdPartyPaymentService {
    void makePayment(double amount);
}

// 第三方支付服务实现
class ThirdPartyPaymentServiceImpl implements ThirdPartyPaymentService {
    @Override
    public void makePayment(double amount) {
        System.out.println("Making payment of $" + amount + " via third party payment service...");
        // 实际支付逻辑
    }
}

// 第三方支付服务适配器
class ThirdPartyPaymentAdapter implements PaymentService {
    private ThirdPartyPaymentService thirdPartyPaymentService;

    public ThirdPartyPaymentAdapter(ThirdPartyPaymentService thirdPartyPaymentService) {
        this.thirdPartyPaymentService = thirdPartyPaymentService;
    }

    @Override
    public void pay(double amount) {
        thirdPartyPaymentService.makePayment(amount);
    }
}


// 客户端代码
public class AdapterExample {
    public static void main(String[] args) {
        // 创建第三方支付服务对象
        ThirdPartyPaymentService thirdPartyPaymentService = new ThirdPartyPaymentServiceImpl();

        // 创建适配器
        PaymentService paymentService = new ThirdPartyPaymentAdapter(thirdPartyPaymentService);

        // 使用适配器进行支付
        paymentService.pay(100.0);
    }
}

在这个示例中,PaymentService 是标准的支付接口,ThirdPartyPaymentService 是第三方支付服务提供的接口,两者不兼容。通过创建一个适配器 ThirdPartyPaymentAdapter,将第三方支付服务的接口适配成标准支付接口,从而与现有的支付系统进行集成。这样,客户端代码可以直接使用标准的支付接口进行支付,而无需了解第三方支付服务的具体实现细节。

8. 装饰器模式

        装饰器模式相当于给对象包装了一层,并且是在不改变原始对象的基础上包装,进行增强。

        应用场景:使用装饰器模式来实现在图像显示前,对其进行加密功能

// 图像接口
interface Image {
    void display();
}

// 具体图像类
class ImageImpl implements Image {
    @Override
    public void display() {
        System.out.println("显示图片...");
    }
}

// 图片加密装饰器
class EncryptionDecorator implements Image {
    private Image image;

    public EncryptionDecorator(Image image) {
        this.image = image;
    }

    @Override
    public void display() {
        // 加密处理
        System.out.println("对图像进行加密...");
        image.display();
    }
}

// 客户端代码
public class DecoratorExample {
    public static void main(String[] args) {
        // 创建具体图像对象
        Image imageImpl = new ImageImpl();

        // 使用加密装饰器包装具体图像对象
        Image imageEncryptionDecorator = new EncryptionDecorator(imageImpl);

        // 显示加密后的图像
        imageEncryptionDecorator.display();
    }
}

代理模式和装饰器模式有什么区别?

  • 在目的上,代理模式的目的是控制对对象的访问,通常是因为你不能或不想直接访问该对象;装饰器模式的目的主要是包装对象,增强对象的功能。
  • 在客户端访问上,代理模式中客户端通常不知道原对象,只知道代理对象;装饰器模式中,客户端通常需要用装饰器包装原对象,因此必须能访问原对象。
  • 在数量上,在代理模式中一个对象只有一个代理;在装饰器模式中,一个对象可以有多个装饰器

9. 观察者模式

        观察者模式也称为发布订阅模式。当Subject(主题)状态发生变化时,它会通知所有的Observe(观察者)。

        应用场景:当一个对象的状态发生变化,需要通知多个对象就可以使用观察者模式。比如股票价格发生变化,通知投资者就可以使用观察者模式。

import java.util.ArrayList;
import java.util.List;

// 主题接口
interface Subject {
    void addObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers();
}

// 观察者接口
interface Observer {
    void update();
}

// 具体主题实现
class ConcreteSubject implements Subject {
    private List<Observer> observers = new ArrayList<>();

    @Override
    public void addObserver(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update();
        }
    }

    // 具体业务逻辑,当状态变化时通知观察者
    public void doSomething() {
        // 执行某些操作
        System.out.println("Subject: Something happened.");
        // 通知观察者
        notifyObservers();
    }
}

// 具体观察者实现
class ConcreteObserver implements Observer {
    @Override
    public void update() {
        System.out.println("Observer: I received the update from the subject.");
    }
}

public class ObserverPatternExample {
    public static void main(String[] args) {
        // 创建主题和观察者对象
        ConcreteSubject subject = new ConcreteSubject();
        ConcreteObserver observer1 = new ConcreteObserver();
        ConcreteObserver observer2 = new ConcreteObserver();

        // 注册观察者
        subject.addObserver(observer1);
        subject.addObserver(observer2);

        // 主题状态变化,通知观察者
        subject.doSomething();
    }
}

观察者模式举例:比如创建订单后,要给用户发短信通知、微信公众号通知等,为了实现接口就可以使用监听器。监听器本质上就是观察者模式。

(1)创建事件,继承ApplicationEvent接口

(2)创建事件监听器,主要是短信发送监听、微信公众号发送监听

(3)发布事件

/**
 * 订单创建事件
 */
public class OrderCreateEvent extends ApplicationEvent {
 
    private String orderInfo;//订单信息
    public OrderCreateEvent(Object source,String orderInfo){
        super(source);
        this.orderInfo = orderInfo;
    }
 
    public String getOrderInfo() {
        return orderInfo;
    }
 
    public void setOrderInfo(String orderInfo) {
        this.orderInfo = orderInfo;
    }
}
//创建监听器

/**
 *  SmsListener短信发送监听器:监听订单创建时间
 */
@Component
public class SmsListener implements ApplicationListener<OrderCreateEvent> {
    @Override
    public void onApplicationEvent(OrderCreateEvent event) {
        //. 发送短信: 调用短信服务,给手机号发送短信信息.
        System.out.println("发送短信 - 调用短信服务,给手机号发送短信信息;订单信息:"+event.getOrderInfo());
    }
}



/**
 *  WechatListener微信通知监听器:监听订单创建时间
 */
@Component
public class WechatListener implements ApplicationListener<OrderCreateEvent> {
    @Override
    public void onApplicationEvent(OrderCreateEvent event) {
        //. 发送微信 - 调用微信公众号的通知服务,进行发送。
        System.out.println("发送微信 - 调用微信公众号的通知服务,进行发送;订单信息:"+event.getOrderInfo());
    }
}
/**
 * 订单服务:发布事件
 */
@Service
public class OrderService {
    @Autowired
    private ApplicationContext applicationContext;
 
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;
 
    /**
     * 创建订单.
     */
    public void createOrder(){
        //1. 创建订单: 生成订单信息,然后保存到数据库.
        System.out.println("创建订单 - 生成订单信息,然后保存到数据库");
 
 
        //2. 发布事件
        OrderCreateEvent orderCreateEvent = new OrderCreateEvent(this,"orderNo:20230815");
        applicationContext.publishEvent(orderCreateEvent);
 
        //applicationEventPublisher.publishEvent(orderCreateEvent);//也可以
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值