设计模式 最新 hot

本文介绍了设计模式的三大类别,包括创建型模式(如单例模式、工厂模式)、结构型模式(如适配器模式、装饰器模式)和行为型模式(如策略模式、观察者模式)。同时,文章也探讨了面向对象编程的七大原则,如单一职责原则、开闭原则等。此外,详细讲解了单例模式、工厂模式(简单工厂和抽象工厂)和代理模式的实现和应用场景,展示了设计模式如何提升代码的灵活性、可维护性和降低复杂性。
摘要由CSDN通过智能技术生成

设计模式

设计模式可以分为三大类:

  1. 创建型模式:创建型模式关注如何创建对象,包括对象的实例化和组合。它们提供了灵活的方法来创建对象,而无需直接实例化对象,从而使系统更加具有扩展性和可维护性。常见的创建型模式包括:工厂方法模式、抽象工厂模式、单例模式、建造者模式和原型模式。

  2. 结构型模式:结构型模式关注对象之间的组合和关联关系,以实现更大型的结构。它们提供了一种简化系统结构的方法,通过定义接口和对象之间的显式关系,从而使系统更加灵活和可扩展。常见的结构型模式包括:适配器模式、桥接模式、装饰器模式、组合模式、外观模式、享元模式和代理模式。

  3. 行为型模式:行为型模式关注对象之间的通信和协作,以实现系统中各个对象的动态行为。它们提供了一种可复用的设计解决方案,以处理对象之间的通信和交互,使系统更加灵活和可复用。常见的行为型模式包括:观察者模式、模板方法模式、策略模式、职责链模式、命令模式、状态模式、访问者模式、迭代器模式和中介者模式等。



面向对象编程(OOP)原则

  1. 单一职责原则(Single Responsibility Principle,SRP):一个类应该只有一个引起变化的原因,即一个类应该只有一个职责。这个原则强调了类的内聚性,每个类应该只专注于完成单一的任务。

  2. 开放封闭原则(Open Closed Principle,OCP):软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。这个原则强调了使用抽象、接口和多态等技术,以便可以增加新的功能,而无需修改现有代码。这样做可以有效地减少引入错误的风险。

  3. 里氏替换原则(Liskov Substitution Principle,LSP):子类应该能够替换父类并保持程序的逻辑行为不变。即,任何基类可以被其子类无缝替换,而不会影响程序的正确性。

  4. 依赖倒置原则(Dependency Inversion Principle,DIP):高层模块不应该依赖于低层模块,它们应该依赖于抽象。抽象不应该依赖于具体实现细节,而是具体实现细节应该依赖于抽象。这个原则通过使用接口或抽象类作为依赖关系的接口,实现了高层模块和低层模块之间的解耦。

  5. 接口隔离原则(Interface Segregation Principle,ISP):客户端不应该依赖它不需要的接口。这个原则强调了保持接口的单一性和最小化的设计,以避免类和模块之间的臃肿依赖。

  6. 合成/聚合复用原则(Composition/Aggregation Reuse Principle,CARP):应优先使用组合和聚合关系,而不是继承关系来实现代码的复用。通过使类之间的关系更加灵活和松耦合,可以提高代码的可维护性和可扩展性。

  7. 迪米特法则(Law of Demeter,LoD):一个对象应当尽可能少地与其他对象发生相互作用。这个原则强调了模块的独立性,每个对象只需要与其直接的朋友进行通信,而不需要了解整个系统的复杂性。

七大原则都是为了 :

  • 减少代码的耦合性。
  • 提高代码的灵活性。
  • 降低代码的复杂性。


1. 单例模式

保证一个类只有一个实例,并提供一个全局访问点来访问该实例

单例模式包含以下要素:

  1. 私有化构造函数:确保该类不能被外部实例化。

  2. 静态私有成员变量:用于保存单例实例,并私有化,确保只能在类内部访问。

  3. 静态公有方法:用于获取单例实例,一般命名为getInstance()

volatile和双重检查锁定机制实现的预加载模式的示例

public class Singleton2 {
     Singleton2 (){}
    //  懒加载模式(Lazy Initialization)是指在需要的时候才创建对象实例。  volatile  确保多线程之间的可见性。
    private static  volatile Singleton2  leton;
    // 预预加载模式(Eager Initialization)是指在应用程序启动时就创建对象实例,无论后续是否会使用到该对象。
    // private static  volatile Singleton2 leton = new Singleton2();;
     public   static  Singleton2 getInstance(){
         //第一次检查:如果leton为null,表示还没有创建对象实例 进入同步代码块。
         if(leton==null){
             synchronized (Singleton2.class){
         //第二次检查:再次判断leton是否为null,避免在同步块外等待线程被另一个已创建的线程所修改。在同步块内,创建一个新的Singleton2对象并赋值给leton。
                 if(leton==null){
                 leton = new Singleton2();
                 }
             }
         }
         return leton;
     }
}

2. 工厂模式

一种创建型设计模式,它提供了一种创建对象的接口,但具体创建哪个对象由子类决定。

工厂模式的优点包括:

  1. 将对象的创建与使用分离,客户端通过抽象工厂来创建对象,可以减少耦合。

  2. 可以轻松添加新的产品,只需要扩展具体产品类和具体工厂类即可。

2.1 简单的工厂模式

interface Product2 {
     void use();
}
class  ProduceA implements Product2 {
    @Override
    public void use() {
        System.out.println("规范产品A");
    }
}
class ProduceB implements Product2 {
    @Override
    public void use() {
        System.out.println("规范产品B");
    }
​
}
  class ProductFactory2 {
        public static  Product2 createProduce(String arg){
            if(arg.equals("A")){
                return new ProduceA();
            }else if(arg.equals("B")){
                return new ProduceB();
            }else {
                throw new IllegalArgumentException("类型参数错误");
            }
        }
​
      public static void main(String[] args) {
          Product2 a = ProductFactory2.createProduce("A");
          a.use();
      }
}

工厂模式包含以下要素:

  1. 抽象产品(Product):定义了产品的接口,具体产品需要实现该接口。

  2. 具体产品(Concrete Product):实现了抽象产品接口的具体类。

  3. 抽象工厂(Factory):定义了创建产品的接口,可以是接口或者抽象类。

  4. 具体工厂(Concrete Factory):实现抽象工厂的具体类,负责创建具体产品。

2.2 抽象工厂模式

// 抽象产品类 - 土豆
interface Produce3 {
    void potato();
}
​
​
// 抽象类工厂 - 土豆工厂
interface FactoryPotato {
    Produce3 productType();
}
​
// 具体产品类 - 土豆块
class PotatoBlocks implements Produce3 {
​
    @Override
    public void potato() {
        System.out.println(" I like potatoblocks");
    }
}
​
// 具体产品类 - 土豆泥
class MashedPotato implements Produce3 {
​
    @Override
    public void potato() {
        System.out.println(" I like mashedpotato");
    }
}
​
// 具体工厂类 - 土豆块加工厂
class BlocksProcessingPlant implements FactoryPotato {
​
    @Override
    public Produce3 productType() {
        return new PotatoBlocks();
    }
}
​
// 具体工厂类 - 土豆泥加工厂
class MashedProcessingPlant implements FactoryPotato {
​
    @Override
    public Produce3 productType() {
        return new MashedPotato();
    }
}
​
public class AbstactFactoryExample2 {
​
    public static void main(String[] args) {
        // 创建具体工厂类
        BlocksProcessingPlant blocksProcessingPlant = new BlocksProcessingPlant();
        MashedProcessingPlant mashedProcessingPlant = new MashedProcessingPlant();
        // 通过具体工厂类对象 调用 具体产品对象 并使用 产品对象
        blocksProcessingPlant.productType().potato();
        mashedProcessingPlant.productType().potato();
​
    }
}
​
// I like potatoblocks
// I like mashedpotato

3. 建造者模式(Builder Pattern) 生成器模式

生成器模式(Builder Pattern)是一种创建型设计模式,用于创建复杂对象。它提供了一种分步骤构建对象的解决方案,使得同样的构建过程可以创建不同的表示。

生成器模式适用于需要创建具有多个可选参数的复杂对象,同时可以避免构造函数的参数列表过长和构造函数重载的问题。它将对象的构建过程封装在一个Builder类中,让客户端可以按需选择和设置参数,最终通过调用build方法返回构建好的对象。

生成器模式的主要角色包括:

  1. Product(产品):表示要建造的复杂对象。

  2. Builder(构建者):负责构建产品的抽象接口,定义了设置参数的方法。

  3. ConcreteBuilder(具体构建者):实现Builder接口,负责具体的构建过程。

  4. Director(指挥者):负责控制构建的流程,根据不同的顺序和方式调用Builder接口的方法。

@Data
public class BuilderPattenCar {
    private String engine;
    private String wheels;
    private String color;
​
    @Override
    public String toString() {
        return "Car: Engine(" + engine + "), Wheels(" + wheels + "), Color(" + color + ")";
    }
}
​
// Builder(构建者)
class CarBuilder {
    private BuilderPattenCar car;
    // 构造函数中创建具体产品对象
     CarBuilder() {
        this.car = new BuilderPattenCar();
    }
    // 设置引擎
     CarBuilder withEngine(String engine) {
        car.setEngine(engine);
        return this;
    }
    // 设置车轮
     CarBuilder withWheels(String wheels) {
        car.setWheels(wheels);
        return this;
    }
    // 设置颜色
     CarBuilder withColor(String color) {
        car.setColor(color);
        return this;
    }
    // 返回构建好的产品对象
     BuilderPattenCar build() {
        return car;
    }
}
​
// 示例
class Mains {
    public static void main(String[] args) {
        BuilderPattenCar car = new CarBuilder()
                .withEngine("V8")
                .withWheels("17-inch")
                .withColor("Red")
                .build();
​
        System.out.println(car);
    }
}
// Car: Engine(V8), Wheels(17-inch), Color(Red)

总结

  • 生成器模式的优点包括
  1. 可以灵活地构建复杂对象,避免构造函数的参数列表过长和构造函数重载的问题。

  2. 客户端代码与具体的构建过程解耦,可以独立变化和复用。

  3. 可以通过构建不同的Builder对象来创建不同的表示,增加了灵活性。

  • 生成器模式的缺点包括:
  1. 需要定义多个类来实现不同的构建过程,增加了代码量和复杂度。

  2. 构建过程中需要额外的内存空间存储中间结果,增加了内存消耗。

4. 适配器模式

适配器模式(Adapter Pattern)是一种结构型设计模式,它用于将一个类的接口转换成客户端所期望的另一个接口。适配器模式让原本不兼容的接口能够一起工作,它相当于一个中间层。

适配器模式有以下几个主要角色:

  1. 目标接口(Target):定义客户端所期望的接口,这是客户端代码所调用的接口。

  2. 适配器(Adapter):它将源接口转换成目标接口。适配器代表了一个已经存在的类,它包含了一个中间层,用于将目标接口转换成适配者接口,并将客户端的请求传递给适配者。

  3. 适配者(Adaptee):它是被适配的类,实际上实现了客户端所期望的功能,但由于接口与目标接口不兼容,无法直接被客户端使用。

  1. 定义目标接口 Target,其中包含客户端所期望的方法。

public interface Target {
    void request();
}
  1. 创建适配者类 Adaptee,其接口与目标接口不兼容。

public class Adaptee {
    public void specificRequest() {
        System.out.println("Adaptee specific request");
    }
}
​
  1. 创建适配器类 Adapter,实现目标接口,并在其内部持有一个适配者对象。在目标接口的方法中调用适配者对象的方法。

public class Adapter implements Target {
    private Adaptee adaptee;
    
    public Adapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }
​
    @Override
    public void request() {
        adaptee.specificRequest();
    }
}
 
  1. 编写客户端代码,使用适配器来调用目标接口的方法。

public class Client {
    public static void main(String[] args) {
        Adaptee adaptee = new Adaptee();
        Target target = new Adapter(adaptee);
        
        target.request();
    }
}
  //  Adaptee specific request

5. 装饰者模式

装饰者模式是一种结构型设计模式,它允许向一个现有的对象添加新的功能,同时又不改变其结构。装饰者模式在不修改原有对象的情况下,通过包装(装饰)对象来进行功能的添加和扩展。

在装饰者模式中,有以下角色:

  • Component(抽象构件):定义了被装饰对象和装饰对象的公共接口,可以是抽象类或接口。

  • ConcreteComponent(具体构件):实现抽象构件接口,是被装饰的对象。

  • Decorator(抽象装饰者):继承或实现抽象构件,持有一个被装饰对象的引用,并定义了与抽象构件接口一致的接口。

  • ConcreteDecorator(具体装饰者):实现抽象装饰者接口,具体实现装饰功能并调用被装饰对象的方法。

首先,我们定义一个抽象构件(Component)接口,其中包含一个用于执行操作的方法:

public interface Component {
    void operation();
}

然后,我们创建具体构件(ConcreteComponent)类,实现抽象构件接口:

public class ConcreteComponent implements Component {
    @Override
    public void operation() {
        System.out.println("执行具体构件的操作");
    }
}

接下来,我们定义抽象装饰者(Decorator)类,实现抽象构件接口,并持有一个Component对象的引用:

public abstract class Decorator implements Component {
    protected Component component;
​
    public Decorator(Component component) {
        this.component = component;
    }
​
    @Override
    public void operation() {
        component.operation();
    }
}

然后,我们创建具体装饰者(ConcreteDecorator)类,继承抽象装饰者类,并在其中添加额外的功能:

public class ConcreteDecorator extends Decorator {
    public ConcreteDecorator(Component component) {
        super(component);
    }
​
    @Override
    public void operation() {
        System.out.println("执行具体装饰者的操作");
        super.operation();
        // 添加额外功能
        addedBehavior();
    }
​
    public void addedBehavior() {
        System.out.println("执行具体装饰者的额外功能");
    }
}

最后,我们可以在客户端中使用装饰者模式:

public class Client {
    public static void main(String[] args) {
        // 创建具体构件对象
        Component component = new ConcreteComponent();
        // 创建具体装饰者对象,并将具体构件对象传入
        Decorator decorator = new ConcreteDecorator(component);
        // 执行操作
        decorator.operation();
    }
}
// 执行具体装饰者的操作
// 执行具体构件的操作
// 执行具体装饰者的额外功能

以上代码中,首先创建了一个具体构件对象(ConcreteComponent),然后将其传入具体装饰者对象(ConcreteDecorator)中。通过调用具体装饰者对象的operation()方法,实现了装饰功能的添加和扩展。

6. 代理模式

代理模式是一种结构型设计模式,用于为其他对象提供一个代理或占位符,以控制对该对象的访问。代理类通常充当客户端和目标对象之间的中介,并在访问目标对象时提供一些额外的功能。

在代理模式中,抽象角色定义了目标对象和代理对象之间的公共接口。代理角色实现了该接口,并处理客户端的请求。它通常包含对目标对象的引用,以便能够在执行请求前后添加额外的操作。真实角色是代理角色所代表的目标对象。

代理模式有多种变体,包括静态代理和动态代理。静态代理需要为每个目标对象创建一个代理类,而动态代理可以在运行时为接口创建代理类,无需提前编写代码。

6.1 静态代理

首先,定义一个接口(Subject)作为目标对象和代理对象的共同接口:

public interface Subject {
    void doSomething();
}

然后,创建一个具体的目标对象(RealSubject),实现该接口:

public class RealSubject implements Subject {
    @Override
    public void doSomething() {
        System.out.println("RealSubject doSomething");
    }
}

接下来,创建一个代理类(Proxy),实现该接口,并在其内部持有一个目标对象的引用:

public class Proxy implements Subject {
    private Subject realSubject;
​
    public Proxy(Subject realSubject) {
        this.realSubject = realSubject;
    }
​
    @Override
    public void doSomething() {
        // 在执行目标对象方法前添加额外的操作
        System.out.println("Proxy doSomething before");
        // 调用目标对象方法
        realSubject.doSomething();
        // 在执行目标对象方法后添加额外的操作
        System.out.println("Proxy doSomething after");
    }
}

最后,我们可以在客户端中使用代理类:

public class Client {
    public static void main(String[] args) {
        // 创建目标对象
        Subject realSubject = new RealSubject();
        // 创建代理对象,传入目标对象
        Proxy proxy = new Proxy(realSubject);
        // 调用代理对象的方法
        proxy.doSomething();
    }
}
// Proxy doSomething before
// RealSubject doSomething
// Proxy doSomething after

6.2 动态代理

首先,定义一个公共接口(Subject):

public interface Subject {
    void doSomething();
}

然后,创建一个具体的目标对象(RealSubject),实现该接口:

public class RealSubject implements Subject {
    @Override
    public void doSomething() {
        System.out.println("RealSubject doSomething");
    }
}

接下来,创建一个实现InvocationHandler接口的代理类(ProxyHandler),用于处理对目标对象方法的调用:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
​
public class ProxyHandler implements InvocationHandler {
    private Object target;
​
    public ProxyHandler(Object target) {
        this.target = target;
    }
​
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 在执行目标对象方法前添加额外的操作
        System.out.println("Proxy doSomething before");
        // 调用目标对象方法
        Object result = method.invoke(target, args);
        // 在执行目标对象方法后添加额外的操作
        System.out.println("Proxy doSomething after");
        return result;
    }
}

最后,我们可以在客户端使用动态代理:

import java.lang.reflect.Proxy;
​
public class Client {
    public static void main(String[] args) {
        // 创建目标对象
        Subject realSubject = new RealSubject();
        // 创建InvocationHandler对象
        InvocationHandler handler = new ProxyHandler(realSubject);
        // 创建动态代理对象
        Subject proxy = (Subject) Proxy.newProxyInstance(
                realSubject.getClass().getClassLoader(),
                realSubject.getClass().getInterfaces(),
                handler
        );
        // 调用动态代理对象的方法
        proxy.doSomething();
    }
}
// Proxy doSomething before
// RealSubject doSomething
// Proxy doSomething after

示例中,我们使用了Java内置的Proxy类和InvocationHandler接口来实现动态代理。通过调用Proxy.newProxyInstance()方法,我们可以在运行时动态生成代理对象,并将目标对象和代理处理程序传递给它。代理对象将在调用其方法时调用InvocationHandler对象的invoke()方法,从而执行传入的目标对象的方法。这里的ProxyHandler类实现了InvocationHandler接口,并在invoke()方法中添加了额外的操作。

动态代理相比于静态代理更加灵活,无需预先定义接口和代理类,可以在运行时根据需求生成代理对象。

7. 策略模式

策略模式(Strategy Pattern)是一种行为设计模式,它允许在运行时根据不同的情况选择算法的行为。

在策略模式中,我们将可变的行为封装成不同的策略(Strategy),每个策略对应一个具体的算法。这些策略可以互相替换,而客户端代码则通过上下文(Context)来使用这些策略,而无需关心具体使用哪个策略。

     +-------------+
        |   Context   |
        +-------------+
              |
              v
        +-------------+
   +----|   Strategy 1   |
   |    +-------------+
   |
   |    +-------------+
   +----|   Strategy 2   |
        +-------------+
​

在策略模式中,Context(上下文)类持有一个Strategy(策略)接口的引用,并在需要的时候调用该接口的方法来执行具体的算法。不同的策略类实现了同一个策略接口,从而可以轻松替换不同的策略。

下面是一个使用策略模式的示例,演示如何使用不同的排序策略对数组进行排序:

首先,定义一个排序策略接口(SortStrategy):

public interface SortStrategy {
    void sort(int[] array);
}

然后,创建不同的具体策略类,实现排序策略接口:

public class BubbleSortStrategy implements SortStrategy {
    @Override
    public void sort(int[] array) {
        System.out.println("BubbleSortStrategy");
        // 实现冒泡排序算法
        // ...
    }
}
​
public class QuickSortStrategy implements SortStrategy {
    @Override
    public void sort(int[] array) {
        System.out.println("QuickSortStrategy");
        // 实现快速排序算法
        // ...
    }
}
​
public class MergeSortStrategy implements SortStrategy {
    @Override
    public void sort(int[] array) {
        System.out.println("MergeSortStrategy");
        // 实现归并排序算法
        // ...
    }
}

接下来,创建上下文类(SortContext),它持有一个排序策略的引用,并通过该引用来调用具体的排序策略:

public class SortContext {
    private SortStrategy strategy;
​
    public SortContext(SortStrategy strategy) {
        this.strategy = strategy;
    }
​
    public void setStrategy(SortStrategy strategy) {
        this.strategy = strategy;
    }
​
    public void sort(int[] array) {
        strategy.sort(array);
    }
}

最后,我们可以在客户端使用策略模式:

public class Client {
    public static void main(String[] args) {
        int[] array = {5, 10, 1, 7, 3};
        
        // 使用冒泡排序策略
        SortStrategy bubbleSortStrategy = new BubbleSortStrategy();
        SortContext context = new SortContext(bubbleSortStrategy);
        context.sort(array);
        
        // 使用快速排序策略
        SortStrategy quickSortStrategy = new QuickSortStrategy();
        context.setStrategy(quickSortStrategy);
        context.sort(array);
        
        // 使用归并排序策略
        SortStrategy mergeSortStrategy = new MergeSortStrategy();
        context.setStrategy(mergeSortStrategy);
        context.sort(array);
    }
}
// BubbleSortStrategy
// QuickSortStrategy
// MergeSortStrategy

在这个示例中,我们定义了一个排序策略接口(SortStrategy),并实现了三种不同的排序策略(冒泡排序、快速排序和归并排序)。上下文类(SortContext)持有一个策略接口的引用,并通过该引用来调用具体的排序策略。客户端可以根据实际需要选择不同的排序策略,而无需修改原有的代码。

策略模式的优点包括增加了代码的灵活性和可扩展性,将可变的行为封装成策略类,避免了代码中大量的条件判断。同时也符合开闭原则,因为可以通过增加新的策略类来扩展系统的功能。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值