三、常见设计模式_09(笔记)_@sneakythrows是代理模式吗-程序员宅基地

技术标签: java  笔记  设计模式  

Java常见设计模式总结

一、概念

设计模式(Design pattern)是解决软件开发某些特定问题而提出的一些解决方案也可以理解成解决问题的一些思路。

作用:可以帮助我们增强代码的可重用性、可扩充性、 可维护性、灵活性好

目的:实现代码的高内聚、低耦合

贯穿思想:面向接口编程,最大限度的适用变化、实现代码复用。

二、分类

针对不同类型的问题,总结出 23 种解决方案,这 23 种解决方案就是我们所说的 23 种设计模式。

image-20231006115523030

其实还有两类:并发型模式和线程池模式,用一个图片来整体描述一下:

image-20231006183810838)

23 种设计默认可以归纳为 3 类:创建型(Creational)结构型 (Structural)行为型(Behavioral)

image-20231006115612930

1. **创建型模式:**对象实例化的模式,创建型模式用于解耦对象的实例化过程。

2. 结构型模式:把类或对象结合在一起形成一个更大的结构。

3. 行为型模式:类和对象如何交互,及划分责任和算法

1:创建型模式:都是用来帮助我们创建对象的、

image-20231006115750536

2:结构性模式:关注对象和类的组织

image-20231006115818780

3:行为型模式:关注系统中对象之间的相互交换,研究系统在运行时对象之间的相互通信和协作,进一步明确对象的职责

image-20231006115903015

三、设计模式 7 大原则

image-20231006120455873

1、 单一职责原则

基本介绍

对类来说的,即一个类应该只负责一项职责。如类A负责两个不同职责:职责1,职责2。

当职责1需求变更而改变A时,可能造成职责2执行错误,所以需要将类A的粒度分解为 A1,A2。

注意事项和细节

  1. 降低类的复杂度,一个类只负责一项职责,这个需要结合实际的开发需求。

  2. 提高类的可读性,可维护性。

  3. 降低变更引起的风险。

  4. 通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级违反单一职责原则;只有类中方法数量足够少,可以在方法级别保持单一职责原则。

2、开闭原则

基本介绍

  1. 开闭原则(Open Closed Principle)是编程中最基础、最重要的设计原则。

  2. 一个软件实体如类,模块和函数应该对扩展开放(对提供方),对修改关闭(对使用方)。用抽象构建框架,用实现扩展细节。

  3. 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化,软件中的对象(类、模块、函数等等)应该对于扩展是开放的,但是对于修改时封闭的。

  4. 编程中遵循其它原则,以及使用设计模式的目的就是遵循开闭原则。

实际案例,例如需要重新计算三角形的面积(提高常量的精度),可以使用继承的方式对类的功能进行扩展,在新类中重写计算面积的方法。有些只需要在本类中完善即可,这个要看实际的情况。

3、里氏替换原则

基本介绍

里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,在适当的情况下,可以通过聚合,组合,依赖 来解决问题。

以下程序引出的问题和思考

image-20231006120950995

解决方法

  1. 我们发现原来运行正常的相减功能发生了错误。原因就是类B无意中重写了父类的方法,造成原有功能出现错误。在实际编程中,我们常常会通过重写父类的方法完成新的功能,这样写起来虽然简单,但整个继承体系的复用性会比较差。特别是运行多态比较频繁的时候。

2)通用的做法是:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉, 采用依赖,聚合,组合等关系代替。

4、依赖倒转原则(DIP)

基本介绍

  1. 高层模块不应该依赖低层模块,二者都应该依赖其抽象。

  2. 抽象不应该依赖细节,细节应该依赖抽象。

  3. 依赖倒转(倒置)的中心思想是面向接口编程。

  4. 依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在java中,抽象指的是接口或抽象类,细节就是具体的实现类。

  5. 使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。

注意事项和细节

  1. 低层模块尽量都要有抽象类或接口,或者两者都有,程序稳定性更好。

  2. 变量的声明类型尽量是抽象类或接口, 这样我们的变量引用和实际对象间,就存在 一个缓冲层,利于程序扩展和优化。

  3. 继承时遵循里氏替换原则。

5、接口隔离原则

基本介绍

客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上

image-20231006121400946

分析

1)类A通过接口Interface1依赖类B,类C通过 接口Interface1依赖类D,如果接口 Interface1对于类A和类C来说不是最小接口, 那么类B和类D必须去实现他们不需要的方法。

2)按隔离原则应当这样处理。

将接口Interface1拆分为独立的几个接口, 类A和类C分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则。

接口隔离原则

应传统方法的问题和使用接口隔离原则改进

1)类A通过接口Interface1依赖类B,类C通过接口Interface1依赖类D,如果接口 Interface1对于类A和类C来说不是最小接口,那么类B和类D必须去实现他们不 需要的方法。

2)将接口Interface1拆分为独立的几个接口,类A和类C分别与他们需要的接口建立 依赖关系。也就是采用接口隔离原则。

3)接口Interface1中出现的方法,根据实际情况拆分为三个接口。

image-20231006121400946

6、合成复用原则

原则是尽量使用合成/聚合的方式,而不是使用继承。

核心思想

  1. 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。

  2. 针对接口编程,而不是针对实现编程。

  3. 为了交互对象之间的松耦合设计而努力。

4)设计原则应该结合实际场景。

7、迪米特法则

基本介绍

  1. 一个对象应该对其他对象保持最少的了解。

  2. 类与类关系越密切,耦合度越大。

  3. 迪米特法则(Demeter Principle)又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的public 方法,不对外泄露任何信息。

  4. 迪米特法则还有个更简单的定义:只与直接的朋友通信。

注意事项和细节

  1. 迪米特法则的核心是降低类之间的耦合,实现高类聚,低耦合。

  2. 但是注意:由于每个类都减少了不必要的依赖,因此迪米特法则只是要求降低类间(对象间)耦合关系, 并不是要求完全没有依赖关系。

四、常见设计模式

1、单例模式

单例模式是指在整个应用中一个类的对象只允许出现一个(类的对象最多只允许创建一次)

我们在创建一个类的对象时,调用的是类的构造器,所以在单例中类的构造器只允许调用一次

**核心:**构造方法私有化,不允许在外部创建类的实例,而将类的实例构建放到类内部实现,在内部通过编码控制实例的创建只能创建一次。

单例模式的实现:

结构

image-20231006150617064

  • **饿汉模式,**它是一种对象立即加载(对象立即创建)模式,当类被加载时该单例对象就被创建,时间效率比较高,空间效率低)。
public class Instance1 {
    

    /*饿汉(勤汉)模式:它是一种对象立即加载(对象立即创建),当类被加载时,
    该单例对象就被创建,时间效率比较高,空间效率低。
     */

    //静态变量只会被加载一次
    private static Instance1 instance = new Instance1();

    //构造器私有化
    private Instance1() {
    

    }
    // 3 提供返回类对象的静态方法
    public static Instance1 getInstance() {
    
        return instance;
    }
}

测试类

public class App {
    

    private static void a() {
    
        Instance1 instance1 = Instance1.getInstance();
        Instance1 instance2 = Instance1.getInstance();
        System.out.println(instance1);
        System.out.println(instance2);
        System.out.println(instance2 == instance1);
    }

    public static void main(String[] args) {
    
       a();
    }
}

测试结果

image-20231006145737325

  • **懒汉模式,**它使用的是懒加载模式实现的单例,当需要使用类对象时才创建该类的单例对象(时间效率比较低,空间效率高)
//懒汉模式:它使用的是懒加载模式实现的实例,当需要使用类对象时才创建该类的单例对象(时间效率比较低,空间效率高)
public class Instance2 {
    

    //为了不让其他类直接访问该成员	懒汉式单例,在使用时创建对象
    //1、私有静态变量
    private static Instance2 instance;

    //2、将构造器私有化
    public Instance2() {
    
    }

    //3、提供一个静态方法,并返回该类的对象
    public static Instance2 getInstance() {
    
        if (instance == null) {
    
            instance = new Instance2();
        }
        return instance;
    }
}

测试类

public class App {
    

    private static void b() {
    
        Instance2 instance1 = Instance2.getInstance();
        Instance2 instance2 = Instance2.getInstance();
        System.out.println(instance2);
        System.out.println(instance1);
        System.out.println(instance2 == instance1);
    }

    public static void main(String[] args) {
    
  	  b();
    }
}

测试结果

image-20231006150046528

懒加载会出现问题:在多线程的情况下,同时会new多个对象,不满足单例模式。

//懒汉模式:它使用的是懒加载模式实现的实例,当需要使用类对象时才创建该类的单例对象(时间效率比较低,空间效率高)
public class Instance2 {
    

    //为了不让其他类直接访问该成员   懒汉式单例,在使用时创建对象
    //1、私有静态变量
    private static Instance2 instance;

    //2、将构造器私有化
    public Instance2() {
    
    }

    //3、提供一个静态方法,并返回该类的对象
    @SneakyThrows
    public static Instance2 getInstance() {
    
        if (instance == null) {
    
            Thread.sleep(10);
            instance = new Instance2();
        }
        return instance;
    }
}

测试类

public class App {
    

    // 
    @SneakyThrows
    private static void error1() {
    
        AtomicReference<Instance2> instance1 = new AtomicReference<>();
        AtomicReference<Instance2> instance2 = new AtomicReference<>();
        Thread t1 = new Thread(() -> {
    
            instance1.set(Instance2.getInstance());
        });
        Thread t2 = new Thread(() -> {
    
            instance2.set(Instance2.getInstance());
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(instance1.get());
        System.out.println(instance2.get());
        System.out.println(instance1.get() == instance2.get());
    }
    
    public static void main(String[] args) {
    
		error1();
    }
}

测试结果

image-20231006150511907

针对以上出现的问题,有一个解决方案:双重校验锁模式

  • 双检测模式实现单例,它是针对懒汉模式的一种优化,提高效率
// 双重校验锁模式:它是针对懒汉模式的一种优化,提高效率
public class Instance3 {
    

    private static Instance3 instance;
    private static Object obj = new Object();

    public Instance3() {
    

    }

    @SneakyThrows
    public static Instance3 getInstance() {
    
        if (instance == null) {
    //用于除了第一次创建对象的其他人
            synchronized (obj) {
    
                if (instance == null) {
    //用于第一次创建对象
                    Thread.sleep(10);
                    instance = new Instance3();
                }
            }
        }
        return instance;
    }
}

测试类

    @SneakyThrows
    private static void c() {
    
        AtomicReference<Instance3> instance1 = new AtomicReference<>();
        AtomicReference<Instance3> instance2 = new AtomicReference<>();
        Thread t1 = new Thread(() -> {
    
            instance1.set(Instance3.getInstance());
        });
        Thread t2 = new Thread(() -> {
    
            instance2.set(Instance3.getInstance());
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(instance1.get());
        System.out.println(instance2.get());
        System.out.println(instance1.get() == instance2.get());
    }

测试结果

image-20231006145737325

但是对于以上三种遇到反射,就失效了

测试类

public class App {
    

    // 
    @SneakyThrows
    private static void error2() {
    
//        Class c = Class.forName("com.dailyblue.spring.单例模式.Instance1");
//        Class c = Class.forName("com.dailyblue.spring.单例模式.Instance2");
        Class c = Class.forName("com.dailyblue.spring.单例模式.Instance3");
        Constructor constructor = c.getDeclaredConstructor();
        constructor.setAccessible(true);
        Object obj1 = constructor.newInstance();
        Object obj2 = constructor.newInstance();
        System.out.println(obj1);
        System.out.println(obj2);
        System.out.println(obj1 == obj2);
    }

    public static void main(String[] args) {
    
		error2();
    }
}

测试结果

image-20231006152423428

  • 枚举法:最简单,最使用,最推荐的写法,使用枚举实现单例
// 最简单,最使用,最推荐的写法,使用枚举实现单例
public enum Instance4 {
    

    INSTANCE;

    public void a(){
    
        System.out.println("aaaa");
    }

    public void b() {
    
        System.out.println(this);
    }
}

测试类

public class App {
    

    private static void d() {
    
        Instance4 instance1 = Instance4.INSTANCE;
        Instance4 instance2 = Instance4.INSTANCE;
        Instance4 instance3 = Instance4.INSTANCE;
        Instance4 instance4 = Instance4.INSTANCE;
        System.out.println(instance1);
        System.out.println(instance2);
        System.out.println(instance3);
        System.out.println(instance4);
        System.out.println(instance1 == instance2);
        System.out.println(instance3 == instance4);
        System.out.println(instance1 == instance3);
        instance1.a();
        instance1.b();
    }

    public static void main(String[] args) {
    
        d();
    }
}

测试结果

image-20231006152758731

2、工厂模式

工厂模式分为简单工厂模式、工厂方法模式和抽象工厂模式,它们都属于设计模式中的创建型模式。

主要功能:帮助我们把对象的实例化部分抽取了出来

目的:降低系统中代码耦合度,并且增强了系统的扩展性

1. 简单工厂模式

优点:实现对象的创建和对象的使用分离,将对象的创建交给专门的工厂类负责

缺点:在于工厂类不够灵活,增加新的具体产品时需要修改工厂类的判断逻辑代码,而且产品较多时,工厂方法代码逻辑将会非常复杂。

结构

image-20231006155221227

定义一个工厂类

public class PhoneFactory {
    

    public Phone makePhone(String name){
    
        return switch (name) {
    
            case "小米" -> new MiPhone();
            case "华为" -> new MetaPhone();
            case "苹果" -> new IPhone();
            default -> null;
        };
    }
}

定义创建产品对象的接口

//抽象产品
public interface Phone {
    

    void show();
}

创建具体产品实例

public class MiPhone implements Phone{
    

    @Override
    public void show() {
    
        System.out.println("我是小米手机");
    }
}
public class MetaPhone implements Phone{
    

    @Override
    public void show() {
    
        System.out.println("我是华为手机");
    }
}
public class IPhone implements Phone{
    

    @Override
    public void show() {
    
        System.out.println("我是苹果手机");
    }
}

测试类

public class App {
    

    public static void main(String[] args) {
    
        Phone p = new PhoneFactory().makePhone("苹果");
        p.show();
    }
}

测试结果

image-20231006155416658

2. 工厂方法模式

此模式中,通过定义一个抽象的核心工厂类,并定义创建产品对象的接口创建具体产品实例的工作延迟到其工厂子类去完成

好处:核心类只关注工厂类的接口定义,而具体的产品实例交给具体的工厂子类去创建。

当系统需要新增一个产品时,无需修改现有系统代码,只需要添加一个具体产品类和其对应的工厂子类,使系统的扩展性变得很好,符合面向对象编程的开闭原则。

结构

image-20231006161006395

定义一个抽象的核心工厂类

// 抽象工厂
public interface ComputerFactory {
    
    Computer makeComputer();
}

定义创建产品对象的接口

//抽象产品
public interface Computer {
    

    void show();
}

具体的产品实例

public class MacComputer implements Computer {
    
    @Override
    public void show() {
    
        System.out.println("我是苹果电脑");
    }
}
public class MetaComputer implements Computer{
    
    @Override
    public void show() {
    
        System.out.println("我是华为电脑");
    }
}
public class MiComputer implements Computer{
    
    @Override
    public void show() {
    
        System.out.println("我是小米电脑");
    }
}

具体的工厂子类

public class MacFactory implements ComputerFactory{
    

    @Override
    public Computer makeComputer() {
    
        return new MacComputer();
    }
}
public class MateFactory implements ComputerFactory {
    

    @Override
    public Computer makeComputer() {
    
        return new MacComputer();
    }
}
public class MiFactory implements ComputerFactory{
    
    @Override
    public Computer makeComputer() {
    
        return new MiComputer();
    }
}

测试类

public class App {
    
    public static void main(String[] args) {
    
        ComputerFactory factory = new MacFactory();
        factory.makeComputer().show();
    }
}

测试结果

image-20231006161055626

3. 抽象工厂模式

此模式是对工厂方法模式的进一步扩展。在工厂方法模式中,一个具体的工厂负责生产一类具体的产品,即一对一的关系。

但是,如果需要一个具体的工厂生产多种产品对象,那么就需要用到抽象工厂模式了。

结构

image-20231006162128377

定义创建产品对象的接口

// 抽象面条
public interface Noodles {
    
    void shape();
}
// 抽象拌料
public interface MixingMaterials {
    

    void materials();
}

定义一个抽象的核心工厂类

//抽象面馆(工厂)
public interface NoodleRestaurant {
    
    Noodles getNoodles();
    MixingMaterials getMixingMaterials();
}

具体的工厂

//具体面馆(工厂)
public class XiaoZhangNoodleRestaurant implements NoodleRestaurant {
    
    @Override
    public Noodles getNoodles() {
    
        return new Spaghetti();
    }

    @Override
    public MixingMaterials getMixingMaterials() {
    
        return new OilSpillMixingMaterials();
    }
}

具体的对象实例

public class Lasagna implements Noodles{
    
    @Override
    public void shape() {
    
        System.out.println("宽面条");
    }
}
public class Spaghetti implements Noodles{
    
    @Override
    public void shape() {
    
        System.out.println("细面条");
    }
}
public class SpinachNoodles implements Noodles{
    
    @Override
    public void shape() {
    
        System.out.println("菠菜面");
    }
}
public class OilSpillMixingMaterials implements MixingMaterials {
    

    @Override
    public void materials() {
    
        System.out.println("油泼");
    }
}
public class TomatoAndEgg implements MixingMaterials{
    
    @Override
    public void materials() {
    
        System.out.println("西红柿鸡蛋");
    }
}

测试类

public class App {
    
    public static void main(String[] args) {
    
        NoodleRestaurant noodleRestaurant = new XiaoZhangNoodleRestaurant();
        Noodles noodles = noodleRestaurant.getNoodles();
        MixingMaterials mixingMaterials = noodleRestaurant.getMixingMaterials();
        noodles.shape();
        mixingMaterials.materials();
    }
}

测试结果

image-20231006162047352

3、代理模式

代理模式属于结构型设计模式

代理模式是指给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用

通俗的来讲代理模式就是我们生活中常见的中介。

代理模式分类:

1. 静态代理模式

静态代理是指代理类由开发人员创建,在程序运行前代理对象已被创建;

(1) 创建服务类接口

public interface Treat {
    
    void treat();
}

(2) 实现服务类接口(代理的目标对象)

public class Boss implements Treat{
    
    @Override
    public void treat() {
    
        System.out.println("我是老板,今天我请客!");
    }
}

(3)创建代理类(代理对象)

在代理对象中调用目标对象的操作方法,并在目标方法调用前或调用后执行相应的功能

public class Assistant implements Treat {
    

    private Boss boss;

    public Assistant(Boss boss) {
    
        this.boss = boss;
    }

    @Override
    public void treat() {
    
        System.out.println("今天老板不来,我全权负责");
        boss.treat();
        System.out.println("大家吃好喝好");
    }
}

(4)测试类

public class App {
    
    public static void main(String[] args) {
    
        Boss boss = new Boss();
        Assistant assistant = new Assistant(boss);
        assistant.treat();
    }
}

测试结果

image-20231006163549921

2. 动态代理模式(基于 JDK 实现)

动态代理是指代理对象在程序运行时通过反射机制(Proxy)动态创建

动态代理中我们不再需要再手动的创建代理类,我们只需要编写一个**动态处理器(InvocationHandler)**就可以了。真正的代理对象由 JVM 在运行时为我们动态的来创建。

JDK 实现的动态代理只支持接口代理,不支持类的代理

结构

image-20231006165250235

创建服务类接口

public interface Treat {
    
    void treat();
}

实现服务类接口(代理的目标对象)

public class Boss implements Treat{
    
    @Override
    public void treat() {
    
        System.out.println("我是老板,今天我请客!");
    }
}

动态处理器类

public class DynamicProxyHandler implements InvocationHandler {
    

    private Treat boss;

    public DynamicProxyHandler(Treat boss) {
    
        this.boss = boss;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
        System.out.println("我是代理者,我来代表老板");
        Object result = method.invoke(boss, args);
        System.out.println("大家好!");
        return result;
    }
}

测试类

public class App {
    
    public static void main(String[] args) {
    
        Boss boss = new Boss();
        Treat obj = (Treat) Proxy.newProxyInstance(Treat.class.getClassLoader(), new Class[]{
    Treat.class}, new DynamicProxyHandler(boss));
        obj.treat();
    }
}

测试类

image-20231006165625415

3.动态代理模式(CGLIB 代理)

支持使用类的子类做为代理对象。

CGLIB 采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对 final 修饰的类进行代理。

JDK 动态代理与 CGLib 动态代理均是实现 Spring AOP 的基础。Enhancer 是一个非常重要的类,它允许为非接口类型创建一个 JAVA 代理,Enhancer 动态的创建给定类的子类并且拦截代理类的所有的方法,和 JDK 动态代理不一样的是不管是接口还是类它都能正常工作。

结构

image-20231006171258736

创建服务类接口

public interface Treat {
    
    void treat();
}

实现服务类接口(代理的目标对象)

public class Boss implements Treat {
    
    @Override
    public void treat() {
    
        System.out.println("我是老板,请客吃法我掏钱!");
    }
}

创建代理类(代理对象)

public class CgLibProxy implements MethodInterceptor {
    

    private Boss boss;

    public Object getEnhancer(Boss boss) {
    
        this.boss = boss;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.boss.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    
        System.out.println("我今天代表老板");
        Object result = method.invoke(boss, objects);
        System.out.println("再见!");
        return result;
    }
}

测试类

public class App {
    
    public static void main(String[] args) {
    
        Boss boss = new Boss();
        CgLibProxy cgLibProxy = new CgLibProxy();
        Boss obj = (Boss) cgLibProxy.getEnhancer(boss);
        obj.treat();
    }
}

测试结果

image-20231006182216892

4、适配器模式

适配器模式是指将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作。(转换器)

  • 适配器模式既可以作为类结构型模式,也可以作为对象结构型模式
  • 在适配器模式中,我们通过增加一个新的适配器类来解决接口不兼容的问题,使得原本没有任何关系的类可以协同工作。

结构

image-20231006183113914

(1) 电源类(模拟电源,输出电压为 220V)

public class Power {
    
    public int output() {
    
        System.out.println("家庭额定电压是:220V");
        return 220;
    }
}

(2) 手机充电接口(模拟客户接口,输出电压为 5V)

//手机充电接口
public interface PhoneCharge {
    
    int charge();
}

(3) 手机类(模拟客户类,通过手机充电接口为手机充电)

public class Phone {
    

    public void charge(PhoneCharge phoneCharge) {
    
        System.out.println("手机开始充电了");
        int o = phoneCharge.charge();
        System.out.println("充电电压是:" + o + "V");
    }
}

(4) 适配器类(模拟手机充电器,用于将 220V 转换为 5V 电压)

public class PowerAdapter implements PhoneCharge {
    

    private Power power = new Power();

    @Override
    public int charge() {
    
        System.out.println("通过充电器开始给手机充电");
        int ed = power.output();
        System.out.println("开始将" + ed + "V转换成5V");
        return 5;
    }
}

(5) 测试类

public class App {
    
    public static void main(String[] args) {
    
        Phone phone = new Phone();
        PowerAdapter adapter = new PowerAdapter();
        phone.charge(adapter);
    }
}

测试结果

image-20231006183450487

5、策略模式

策略模式(Strategy):定义了一组算法,将每个算法都封装起来,在使用时根据不同的环境使用不同的算法(使用时可以互换),UML 结构图如下:

image-20231006142037560

1. Context 上下文

Context 是实际要处理功能的类,该类中屏蔽了对策略或算法的直接访问,通过抽象策略来访问具体策略(具体策略可能会变化)。

2. Strategy 抽象策略(一般为接口)

Strategy 是对具体策略或算法的抽象

3. ConcreteStrategy 具体策略

ConcreteStrategy 是对抽象策略的实现,即具体算法的实现,实现抽象策略的实现类。

示例:

我们出去旅游的时候可能有很多种出行方式,比如说我们可以坐火车、坐高铁、坐飞机等等。不管我们使用哪一种出行方式,最终的目的地都是一样的。也就是选择不同的方式产生的结果都是一样的。

image-20231006142224878

结构

image-20231006142854894

(1)旅游类(Context)

//Context上下文
public class Travel {
    

    private Strategy strategy;

    public Travel(Strategy strategy) {
    
        this.strategy = strategy;
    }

    public void play() {
    
        System.out.println("我们一起要去三亚玩!");
        strategy.travelTools();
        System.out.println("三亚很好玩!");
    }
}

(2)Strategy 抽象策略

// 出行方式:抽象策略
public interface Strategy {
    

    void travelTools();
}

(3)ConcreteStrategy 具体策略

public class CarStrategy implements Strategy{
    

    @Override
    public void travelTools() {
    
        System.out.println("开车自驾游");
    }
}
public class TrainStrategy implements Strategy{
    

    @Override
    public void travelTools() {
    
        System.out.println("开始坐火车!");
    }
}
public class AircraftStrategy implements Strategy {
    

    @Override
    public void travelTools() {
    
        System.out.println("开始登记,飞机起飞了");
    }
}

测试类

public class App {
    
    public static void main(String[] args) {
    
        CarStrategy car = new CarStrategy();
        TrainStrategy train = new TrainStrategy();
        AircraftStrategy air = new AircraftStrategy();
//        Travel travel1 = new Travel(car);
//        Travel travel2 = new Travel(train);
        Travel travel3 = new Travel(air);
//        travel1.play();
//        travel2.play();
        travel3.play();
    }
}

测试结果

image-20231006143454467

6、责任链模式

**责任链模式:**顾名思义,就是用来处理相关事务责任的一条执行链,执行链上有多个节点,每个节点都有机会处理请求事务,如果某个节点处理完了就可以根据实际业务需求传递给下一个节点继续处理或者返回处理完毕。

示例:

员工请假审批流程:

请假时间 1 天以内,项目组长审批即可

请假时间大于 1 天小于等于 3 天,则需要项目经理审批

请假时间大于 3 天,则需要总经理审批

(1) 抽象处理类

public abstract class Handler {
    

    private Handler nextHandler;

    public Handler(Handler nextHandler) {
    
        this.nextHandler = nextHandler;
    }

    public Handler getNextHandler() {
    
        return nextHandler;
    }

    public void setNextHandler(Handler nextHandler) {
    
        this.nextHandler = nextHandler;
    }

    public abstract void action(int day);
}

(2) 项目组长处理者类

//项目组长
public class PM extends Handler {
    

    public PM(Handler nextHandler) {
    
        super(nextHandler);
    }

    @Override
    public void action(int day) {
    
        System.out.println("小组长开始审批");
        if (day > 1) {
    
            getNextHandler().action(day);
        }
    }
}

(3) 项目经理处理者类

//项目经理
public class ProjectManager extends Handler {
    

    public ProjectManager(Handler nextHandler) {
    
        super(nextHandler);
    }

    @Override
    public void action(int day) {
    
        System.out.println("项目经理开始审批");
        if (day > 3) {
    
            getNextHandler().action(day);
        }
    }
}

(4) 总经理处理者类

//总经理
public class CEO extends Handler {
    


    public CEO(Handler nextHandler) {
    
        super(nextHandler);
    }

    @Override
    public void action(int day) {
    
        System.out.println("总经理开始审批");
    }
}

(5) 测试类

public class App {
    
    public static void main(String[] args) {
    
        CEO ceo = new CEO(null);
        ProjectManager projectManager = new ProjectManager(ceo);
        PM pm = new PM(projectManager);
//        pm.action(1);
//        pm.action(3);
        pm.action(8);
    }
}

测试结果

image-20231006141713726

image-20231006141725150

image-20231006141741579

7、装饰者模式

装饰者模式:动态地将责任(功能)附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

  • 装饰者被装饰对象有相同的超类。

  • 你可以用一个或多个装饰者包装一个对象(被装饰者对象)。

  • 既然装饰者和被装饰对象有相同的超类,所以在任何需要原始对象(被包装的)的场合,可以用装饰过的对象代替它

  • 装饰者可以在所委托被装饰者的行为之前与 / 或之后,加上自己的行为,以达到特定的目的。

  • 在装饰者的行为(方法中)发起对被装饰者对象的行为(方法)的调用

  • 对象可以在任何时候被装饰,所以可以在运行时动态地、不限量地用你喜欢的装饰者来装饰对象。

image-20231006135324670

示例:

定义一个人类

学生继承人类,学生在学习

程序员继承人类,程序员在敲代码

学生和程序员不单单会学习和敲代码他们其中有些人可能还具有其他能力,

如唱歌、跳舞等,我们通过装饰模式的来为不同的人群添加新的能力

结构

image-20231006135652581

(1) 人类,被装饰者的抽象

public interface Person {
    

    void action();
}

(2) 被装饰者的具体类,直接实现 Perosn 接口

public class Programmer implements Person {
    

    private String name;

    public Programmer(String name) {
    
        this.name = name;
    }

    @Override
    public void action() {
    
        System.out.println(name + "正在疯狂刷礼物!");
    }
}
public class Singer implements Person {
    

    private Programmer programmer;

    public Singer(Programmer programmer) {
    
        this.programmer = programmer;
    }

    @Override
    public void action() {
    
        System.out.println("我扩展了程序员的行为");
        programmer.action();
        System.out.println("我们一起唱着歌");
    }
}

(3) 装饰器类的抽象,实现 Person 接口

public class Student implements Person {
    

    private String name;

    public Student(String name) {
    
        this.name = name;
    }

    @Override
    public void action() {
    
        System.out.println(name + "正在跳舞!");
    }
}
public class Student implements Person {
    

    private String name;

    public Student(String name) {
    
        this.name = name;
    }

    @Override
    public void action() {
    
        System.out.println(name + "正在跳舞!");
    }
}

(5) 测试类

public class App {
    
    public static void main(String[] args) {
    
        Programmer programmer = new Programmer("乔杰");
        Singer singer = new Singer(programmer);
        singer.action();
        Student stu = new Student("董雪");
        Dancer dancer = new Dancer(stu);
        dancer.action();
    }
}

测试结果

image-20231006140509061

8、观察者模式

观察者模式:指多个对象之间存在一对多的依赖关系,当一个对象(服务器|主题)的状态发生改变时,所有依赖它的对象(客户端|观察者)都能自动收到通知并根据通知来决定自己行为。这种模式有时又被称为”发布-订阅模式”、“模型-视图模式”。

观察者模式属于行为型模式。观察者模式符合依赖倒置原则

观察者模式中的角色:

1、抽象主题(Subject):

它是观察者关注的事情(观察者关注的某件事),在主题中会定义”注册观察者”、”解除观察者”、”通知观察者”的相关操作,一般使用接口表示。

2、具体主题(Concrete Subject):

具体主题实现抽象主题,并指定具体的关注事件,并设置一个用于存储观察者集合

3、抽象观察者(Observer):

定义观察者收到通知后要做的事情

4、具体观察者(Concrete Observer):

具体观察者实现抽象观察者接口,对接收到通知后要做的事情进行具体实现。

image-20231006132711906

实例:

购买彩票

观察者关注的主题:彩票开奖信息发布

观察者是彩票的购买人

结构

image-20231006134359664

抽象主题:

public interface Subject {
    
    // 注册观察者对象
    void registerObServer(ObServer obServer);
    // 从主题中移除一个观察者对象
    void removeObServer(ObServer obServer);
    // 通知观察者
    void notifyObServer();
}

抽象观察者:

public interface ObServer {
    
    //观察者接收通知的方法
    void update(String message, int num);
}

具体主题(福彩):

public class FuCaiZhuTi implements Subject {
    

    private Integer num;

    public FuCaiZhuTi() {
    
        num = (int) (Math.random() * 100 + 1);
    }
	//使用 list 集合存储所有观察者
    private List<ObServer> list = new ArrayList<>();

    @Override
    public void registerObServer(ObServer obServer) {
    
        //将新的观察者对象存入到观察者集合中
        list.add(obServer);
    }

    @Override
    public void removeObServer(ObServer obServer) {
    
        //观察者集合中存在该观察者对象则移除
        list.remove(obServer);
    }

    @Override
    public void notifyObServer() {
    
       // 通知所有观察者
        list.forEach((e) -> {
    
            e.update("摇奖结果是:" + num, num);
        });
    }
}

具体观察者:

public class CaiMin implements ObServer {
    
    //设置当前观察者所关注的主题
    private Integer num;
    private String name;

    public CaiMin(String name) {
    
        this.name = name;
        num = (int) (Math.random() * 100 + 1);
    }

    @Override
    public void update(String message, int num) {
    
        System.out.println(name + "购买的彩票是:" + this.num);
        System.out.println("彩票中心的摇奖结果是:" + num);
        System.out.println(num == this.num ? "中奖了" : "没中奖");
    }
}

测试类:

public class App {
    
    public static void main(String[] args) throws Exception {
    
        Subject subject = new FuCaiZhuTi();
        for (int i = 1; i < 251; i++) {
    
            CaiMin caiMin = new CaiMin("屈鹏飞" + i);
            subject.registerObServer(caiMin);
        }
        System.out.println("开始摇奖:");
        Thread.sleep(2000);
        subject.notifyObServer();
    }
}

测试结果

image-20231006134304147

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/m0_57948510/article/details/133617118

智能推荐

攻防世界_难度8_happy_puzzle_攻防世界困难模式攻略图文-程序员宅基地

文章浏览阅读645次。这个肯定是末尾的IDAT了,因为IDAT必须要满了才会开始一下个IDAT,这个明显就是末尾的IDAT了。,对应下面的create_head()代码。,对应下面的create_tail()代码。不要考虑爆破,我已经试了一下,太多情况了。题目来源:UNCTF。_攻防世界困难模式攻略图文

达梦数据库的导出(备份)、导入_达梦数据库导入导出-程序员宅基地

文章浏览阅读2.9k次,点赞3次,收藏10次。偶尔会用到,记录、分享。1. 数据库导出1.1 切换到dmdba用户su - dmdba1.2 进入达梦数据库安装路径的bin目录,执行导库操作  导出语句:./dexp cwy_init/[email protected]:5236 file=cwy_init.dmp log=cwy_init_exp.log 注释:   cwy_init/init_123..._达梦数据库导入导出

js引入kindeditor富文本编辑器的使用_kindeditor.js-程序员宅基地

文章浏览阅读1.9k次。1. 在官网上下载KindEditor文件,可以删掉不需要要到的jsp,asp,asp.net和php文件夹。接着把文件夹放到项目文件目录下。2. 修改html文件,在页面引入js文件:<script type="text/javascript" src="./kindeditor/kindeditor-all.js"></script><script type="text/javascript" src="./kindeditor/lang/zh-CN.js"_kindeditor.js

STM32学习过程记录11——基于STM32G431CBU6硬件SPI+DMA的高效WS2812B控制方法-程序员宅基地

文章浏览阅读2.3k次,点赞6次,收藏14次。SPI的详情简介不必赘述。假设我们通过SPI发送0xAA,我们的数据线就会变为10101010,通过修改不同的内容,即可修改SPI中0和1的持续时间。比如0xF0即为前半周期为高电平,后半周期为低电平的状态。在SPI的通信模式中,CPHA配置会影响该实验,下图展示了不同采样位置的SPI时序图[1]。CPOL = 0,CPHA = 1:CLK空闲状态 = 低电平,数据在下降沿采样,并在上升沿移出CPOL = 0,CPHA = 0:CLK空闲状态 = 低电平,数据在上升沿采样,并在下降沿移出。_stm32g431cbu6

计算机网络-数据链路层_接收方收到链路层数据后,使用crc检验后,余数为0,说明链路层的传输时可靠传输-程序员宅基地

文章浏览阅读1.2k次,点赞2次,收藏8次。数据链路层习题自测问题1.数据链路(即逻辑链路)与链路(即物理链路)有何区别?“电路接通了”与”数据链路接通了”的区别何在?2.数据链路层中的链路控制包括哪些功能?试讨论数据链路层做成可靠的链路层有哪些优点和缺点。3.网络适配器的作用是什么?网络适配器工作在哪一层?4.数据链路层的三个基本问题(帧定界、透明传输和差错检测)为什么都必须加以解决?5.如果在数据链路层不进行帧定界,会发生什么问题?6.PPP协议的主要特点是什么?为什么PPP不使用帧的编号?PPP适用于什么情况?为什么PPP协议不_接收方收到链路层数据后,使用crc检验后,余数为0,说明链路层的传输时可靠传输

软件测试工程师移民加拿大_无证移民,未受过软件工程师的教育(第1部分)-程序员宅基地

文章浏览阅读587次。软件测试工程师移民加拿大 无证移民,未受过软件工程师的教育(第1部分) (Undocumented Immigrant With No Education to Software Engineer(Part 1))Before I start, I want you to please bear with me on the way I write, I have very little gen...

随便推点

Thinkpad X250 secure boot failed 启动失败问题解决_安装完系统提示secureboot failure-程序员宅基地

文章浏览阅读304次。Thinkpad X250笔记本电脑,装的是FreeBSD,进入BIOS修改虚拟化配置(其后可能是误设置了安全开机),保存退出后系统无法启动,显示:secure boot failed ,把自己惊出一身冷汗,因为这台笔记本刚好还没开始做备份.....根据错误提示,到bios里面去找相关配置,在Security里面找到了Secure Boot选项,发现果然被设置为Enabled,将其修改为Disabled ,再开机,终于正常启动了。_安装完系统提示secureboot failure

C++如何做字符串分割(5种方法)_c++ 字符串分割-程序员宅基地

文章浏览阅读10w+次,点赞93次,收藏352次。1、用strtok函数进行字符串分割原型: char *strtok(char *str, const char *delim);功能:分解字符串为一组字符串。参数说明:str为要分解的字符串,delim为分隔符字符串。返回值:从str开头开始的一个个被分割的串。当没有被分割的串时则返回NULL。其它:strtok函数线程不安全,可以使用strtok_r替代。示例://借助strtok实现split#include <string.h>#include <stdio.h&_c++ 字符串分割

2013第四届蓝桥杯 C/C++本科A组 真题答案解析_2013年第四届c a组蓝桥杯省赛真题解答-程序员宅基地

文章浏览阅读2.3k次。1 .高斯日记 大数学家高斯有个好习惯:无论如何都要记日记。他的日记有个与众不同的地方,他从不注明年月日,而是用一个整数代替,比如:4210后来人们知道,那个整数就是日期,它表示那一天是高斯出生后的第几天。这或许也是个好习惯,它时时刻刻提醒着主人:日子又过去一天,还有多少时光可以用于浪费呢?高斯出生于:1777年4月30日。在高斯发现的一个重要定理的日记_2013年第四届c a组蓝桥杯省赛真题解答

基于供需算法优化的核极限学习机(KELM)分类算法-程序员宅基地

文章浏览阅读851次,点赞17次,收藏22次。摘要:本文利用供需算法对核极限学习机(KELM)进行优化,并用于分类。

metasploitable2渗透测试_metasploitable2怎么进入-程序员宅基地

文章浏览阅读1.1k次。一、系统弱密码登录1、在kali上执行命令行telnet 192.168.26.1292、Login和password都输入msfadmin3、登录成功,进入系统4、测试如下:二、MySQL弱密码登录:1、在kali上执行mysql –h 192.168.26.129 –u root2、登录成功,进入MySQL系统3、测试效果:三、PostgreSQL弱密码登录1、在Kali上执行psql -h 192.168.26.129 –U post..._metasploitable2怎么进入

Python学习之路:从入门到精通的指南_python人工智能开发从入门到精通pdf-程序员宅基地

文章浏览阅读257次。本文将为初学者提供Python学习的详细指南,从Python的历史、基础语法和数据类型到面向对象编程、模块和库的使用。通过本文,您将能够掌握Python编程的核心概念,为今后的编程学习和实践打下坚实基础。_python人工智能开发从入门到精通pdf

推荐文章

热门文章

相关标签