JAVA设计模式(结合例子理解)

最近在学习Java设计模式,以下是在学习的过程中做的一些总结。

JAVA设计模式:

创建型模式:

创建型模式主要关注于如何有效的实例化对象和类,

一、单例模式:

单例模式分为: 饿汉式单例模式、懒汉式单例模式

这两种单例模式之间的利与弊:懒汉式比饿汉式可以更好地控制,但需要处理好线程同步问题,需要谨慎处理。饿汉式较为简单,无需同步操作,但可能会造成资源的浪费。

线程安全: 线程安全是指在多线程环境下,一个程序、系统、数据结构或对象能够正确地处理多个并发线程的操作,而不会导致数据损坏、不一致性或不可预测的行为。

饿汉式单例模式(线程安全模式):会导致资源浪费,即使不需要单例对象也会被创建。但由于在类加载时就实例化对象,这样可以保证线程安全,基本防止了多线程的同步问题。

public class Singleton {
    private static final Singleton instance = new Singleton();//使用static、final关键字保证了实例的全局唯一性和不可变性

    private Singleton() {
        // 私有构造函数
    }

    public static Singleton getInstance() {
        return instance;
    }
}

懒汉式单例模式(线程安全模式–synchronized方式):可以使用synchronized关键字加锁来保证线程安全,但这样会降低性能,因为每次调用getInstance()方法都会获得该锁,即使实例已经创建。

public class Singleton {
    private static Singleton instance;

    private Singleton() {
        // 私有构造函数
    }

    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

懒汉式单例模式(线程安全模式–静态内部类方式):

public class Singleton {
    private Singleton() {
        // 私有构造函数
    }

    private static class SingletonHolder {//静态内部类会在首次访问时创建出一个实例对象,后续的访问不会创建新的实例。保证线										 程安全
        private static final Singleton instance = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }
}

懒汉式单例模式(非线程安全模式–禁用):

public class Singleton {
    private static Singleton instance;

    private Singleton() {
        // 私有构造函数
    }

    public static Singleton getInstance() {//在多线程的环境下,多个线程同时进入getInstance()方法中,并且instance==null										   时,它们都会创建新的实例,从而破环单例模式的要求。
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

懒汉式单例模式(线程安全模式–双检锁单例模式Double check):

public class Singleton {
    private static volatile Singleton instance;//添加volatile关键字,是为了确保避免指令重排序,所导致的线程安全问题。

    private Singleton() {
        // 私有构造函数
    }

    //第一检查的意义:提高效率。synchronized锁阻挡了多余的线程同时进入同步代码块中。
    public static Singleton getInstance() {
        if (instance == null) {//第一次检查
            synchronized (Singleton.class) {
                if (instance == null) {//第二次检查
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

二、工厂模式:

工厂模式分为: 简单工厂模式、工厂方法模式、抽象工厂模式

开闭原则: 一个软件实体在不修改原有代码的情况下应该能够通过扩展来适应新的需求或变化。开闭原则的目标是促使软件的可维护性、可扩展性和可重用性

简单工厂模式(工厂通过不同的要求(参数)来创建不同的产品):

定义: 简单工厂模式是一种创建对象的设计模式,它通过一个单独的工厂类来创建不同类型的对象,客户端通过向工厂传递相应的参数来获取所需的对象。

结构: 简单工厂包括工厂类、产品接口和具体产品类。

优点: 简单工厂模式将对象的创建过程封装在一个工厂类中,客户端不需要了解对象的具体创建细节。

缺点: 当需要添加新类型的产品时,必须修改工厂类,违反了开闭原则。

// 定义产品接口
interface Product {
    void produce();
}

// 具体产品类
class ConcreteProductA implements Product {
    @Override
    public void produce() {
        System.out.println("生产产品A");
    }
}

class ConcreteProductB implements Product {
    @Override
    public void produce() {
        System.out.println("生产产品B");
    }
}

// 工厂类
class Factory {
    public Product createProduct(String productType) {
        if ("A".equals(productType)) {
            return new ConcreteProductA();
        } else if ("B".equals(productType)) {
            return new ConcreteProductB();
        } else {
            throw new IllegalArgumentException("不支持的产品类型");
        }
    }
}
public class Main {
    public static void main(String[] args) {
        Factory factory = new Factory();

        // 创建产品A
        Product productA = factory.createProduct("A");
        productA.produce(); // 调用产品A的produce方法

        // 创建产品B
        Product productB = factory.createProduct("B");
        productB.produce(); // 调用产品B的produce方法
    }
}

工厂方法模式(工厂分别创建所对应的产品-1对1创建):

定义: 工厂方法模式是一种创建对象的设计模式,它定义了一个创建对象的接口,但将具体的创建过程交给子类来实现。每个具体工厂类负责创建特定类型的产品。

结构: 工厂方法模式包括抽象工厂接口、具体工厂类、产品接口和具体产品类。

优点: 工厂方法模式符合开闭原则,允许添加新的产品类型而无需修改已有代码,每个产品类型有自己的工厂类。

缺点: 相对于简单工厂,需要更多的类和接口,增加了代码复杂度。

// 定义产品接口
interface Product {
    void produce();
}

// 具体产品类
class ConcreteProductA implements Product {
    @Override
    public void produce() {
        System.out.println("生产产品A");
    }
}

class ConcreteProductB implements Product {
    @Override
    public void produce() {
        System.out.println("生产产品B");
    }
}

// 工厂接口
interface Factory {
    Product createProduct();
}

// 具体工厂类
class ConcreteFactoryA implements Factory {
    @Override
    public Product createProduct() {
        return new ConcreteProductA();
    }
}

class ConcreteFactoryB implements Factory {
    @Override
    public Product createProduct() {
        return new ConcreteProductB();
    }
}

public class Main {
    public static void main(String[] args) {
        // 创建工厂A
        Factory factoryA = new ConcreteFactoryA();
        // 工厂A生产产品A
        Product productA = factoryA.createProduct();
        productA.produce();

        // 创建工厂B
        Factory factoryB = new ConcreteFactoryB();
        // 工厂B生产产品B
        Product productB = factoryB.createProduct();
        productB.produce();
    }
}

抽象工厂模式(选择合适的具体工厂类来创建一组相关的产品):

定义: 抽象工厂模式定义了一个抽象工厂接口,该接口包含一组方法,每个方法用于创建一个特定类型的对象。每个具体工厂类实现该接口并提供一组相关对象的创建方法。

结构: 抽象工厂接口、具体工厂类、抽象产品接口、具体产品类

优点: 分离接口和实现、提供一组相关的对象、符合开闭原则

缺点: 抽象工厂模式引入了多个抽象接口和多个具体工厂类,会增加系统的复杂性。切换或者替换一组产品对象可能需要修改客户端源代码,因为客户端通常依赖于具体的工厂类和产品类。

// 定义产品接口
interface ProductA {
    void produce();
}

interface ProductB {
    void produce();
}

// 具体产品类
class ConcreteProductA1 implements ProductA {
    @Override
    public void produce() {
        System.out.println("生产产品A1");
    }
}

class ConcreteProductA2 implements ProductA {
    @Override
    public void produce() {
        System.out.println("生产产品A2");
    }
}

class ConcreteProductB1 implements ProductB {
    @Override
    public void produce() {
        System.out.println("生产产品B1");
    }
}

class ConcreteProductB2 implements ProductB {
    @Override
    public void produce() {
        System.out.println("生产产品B2");
    }
}

// 工厂接口
interface AbstractFactory {
    ProductA createProductA();
    ProductB createProductB();
}

// 具体工厂类1
class ConcreteFactory1 implements AbstractFactory {
    @Override
    public ProductA createProductA() {
        return new ConcreteProductA1();
    }

    @Override
    public ProductB createProductB() {
        return new ConcreteProductB1();
    }
}
//  具体工厂类2
class ConcreteFactory2 implements AbstractFactory {
    @Override
    public ProductA createProductA() {
        return new ConcreteProductA2();
    }

    @Override
    public ProductB createProductB() {
        return new ConcreteProductB2();
    }
}

public class Main {
    public static void main(String[] args) {
        // 创建工厂1
        AbstractFactory factory1 = new ConcreteFactory1();
        // 由工厂1创建产品A和产品B
        ProductA productA1 = factory1.createProductA();
        ProductB productB1 = factory1.createProductB();
        // 生产产品
        productA1.produce();
        productB1.produce();

        // 创建工厂2
        AbstractFactory factory2 = new ConcreteFactory2();
        // 由工厂2创建产品A和产品B
        ProductA productA2 = factory2.createProductA();
        ProductB productB2 = factory2.createProductB();
        // 生产产品
        productA2.produce();
        productB2.produce();
    }
}

三、原型模式:

定义: 通过克隆现有的对象来创建新的对象,无需使用构造函数。通常通过实现Cloneable接口和重写clone()方法来实现。

// 定义一个可克隆的原型接口
interface Prototype extends Cloneable {
    Prototype clone();
}

// 具体原型类
class ConcretePrototype implements Prototype {
    private int value;

    public ConcretePrototype(int value) {
        this.value = value;
    }

    //super关键字,用来调用显示继承的类,对接口不适用。
    @Override
    public Prototype clone() {
        try {
            return (Prototype) super.clone();//浅拷贝,调用的是object中的clone()方法。
        } catch (CloneNotSupportedException e) {
            return null;
        }
    }

    public int getValue() {
        return value;
    }
}

public class PrototypeExample {
    public static void main(String[] args) {
        // 创建原型对象
        Prototype original = new ConcretePrototype(10);

        // 克隆原型对象
        Prototype clone = original.clone();

        // 验证克隆是否成功
        if (clone != null) {
            System.out.println("Original Value: " + original.getValue());
            System.out.println("Clone Value: " + clone.getValue());
        }
    }
}

四、建造者模式:

建造者模式定义:

import lombok.Getter;
//computer类
@Getter
public class Computer {
    private String processor;
    private int memoryGB;;
    private int storageGB;
    private String operatingSystem;
	//如果参数传递不全,会导致问题的发生,参数会设置成默认值返回。
    public Computer(String processor, int memoryGB, int storageGB, String operatingSystem) {
        this.processor = processor;
        this.memoryGB = memoryGB;
        this.storageGB = storageGB;
        this.operatingSystem = operatingSystem;
    }

    // Getter 方法...
}
//computer建造者类
public class ComputerBuilder {
    private String processor;
    private int memoryGB;
    private int storageGB;
    private String operatingSystem;

    public ComputerBuilder setProcessor(String processor) {
        this.processor = processor;
        return this;
    }

    public ComputerBuilder setMemoryGB(int memoryGB) {
        this.memoryGB = memoryGB;
        return this;
    }

    public ComputerBuilder setStorageGB(int storageGB) {
        this.storageGB = storageGB;
        return this;
    }

    public ComputerBuilder setOperatingSystem(String operatingSystem) {
        this.operatingSystem = operatingSystem;
        return this;
    }

    public Computer build() {
        return new Computer(processor, memoryGB, storageGB, operatingSystem);
    }
}
//建造者模式创建对象
Computer gamingPC = new ComputerBuilder()//逐步调用建造者的方式,这使得客户端可以更加灵活的创建对象,只设置他们所关心的属										 性,而不必设置其他属性。
    .setProcessor("Intel Core i9")
    .setMemoryGB(32)
    .setStorageGB(2000)
    .setOperatingSystem("Windows 10")
    .build();

Computer officePC = new ComputerBuilder()
    .setProcessor("Intel Core i5")
    .setMemoryGB(16)
    .setStorageGB(500)
    .setOperatingSystem("Windows 11")
    .build();

结构型模式:

结构型模式的定义:

结构型模式描述了如何组织相关的对象以形成大的系统结构,以便这些对象可以协同工作,并且方便后期维护和扩展。

代理模式与装饰器模式的区别:

代理模式是为了覆盖原有对象的行为,而装饰器模式是未改变原有对象的基础上加上新的行为。

一、代理模式:

代理模式:

代理对象与其代理的真实对象实现的是同一个接口,也就是说我们可以通过代理对象来操作真实的对象,而不需要直接访问真实的对象。

// 1. 创建一个接口,表示门的访问
interface Door {
    void open();
    void close();
}

// 2. 创建真实,实现接口
class LabDoor implements Door {
    @Override
    public void open() {
        System.out.println("实验室的门已打开");
    }

    @Override
    public void close() {
        System.out.println("实验室的门已关闭");
    }
}

// 3. 创建一个代理,实现相同的接口
class SecuredDoor implements Door {
    private Door realDoor;
    private String password;

    public SecuredDoor(Door realDoor, String password) {
        this.realDoor = realDoor;
        this.password = password;
    }

    @Override
    public void open() {
        if (authenticate(password)) {
            realDoor.open();
        } else {
            System.out.println("你无权进入!");
        }
    }

    @Override
    public void close() {
        realDoor.close();
    }

    private boolean authenticate(String password) {
        return password.equals("secret");
    }
}

// 4. 使用代理模式
public class ProxyExample {
    public static void main(String[] args) {
        Door labDoor = new LabDoor();
        Door securedDoor = new SecuredDoor(labDoor, "wrongPassword");
        
        securedDoor.open(); // 输出:"你无权进入!"
        
        securedDoor = new SecuredDoor(labDoor, "secret");
        securedDoor.open(); // 输出:"实验室的门已打开"
        securedDoor.close(); // 输出:"实验室的门已关闭"
    }
}

二、装饰器模式:

​ 在不改变原有对象的基础上,动态的扩展或者改变对象的行为,装饰者和被装饰者通常有个共同的基类,装饰者通过持有一个被装饰者的引用并实现相同的接口,然后可以在调用方法的前后增加新的行为,从而提供新的功能。总结来说是为了扩展。

//Pizza接口
public interface Pizza {
    String getDescription();
    double getCost();
}
//Pizza类型
public class Margherita implements Pizza {
    @Override
    public String getDescription() {
        return "Margherita";
    }

    @Override
    public double getCost() {
        return 6.00;
    }
}

public class VeggieParadise implements Pizza {
    @Override
    public String getDescription() {
        return "Veggie Paradise";
    }

    @Override
    public double getCost() {
        return 7.00;
    }
}
//抽象装饰类
public abstract class PizzaDecorator implements Pizza {
    protected Pizza pizzaToDecorate;

    public PizzaDecorator(Pizza pizzaToDecorate) {
        this.pizzaToDecorate = pizzaToDecorate;
    }

    @Override
    public String getDescription() {
        return pizzaToDecorate.getDescription();
    }

    @Override
    public double getCost() {
        return pizzaToDecorate.getCost();
    }
}
//具体的装饰类,就像配料一样
public class RoastedPeppers extends PizzaDecorator {
    public RoastedPeppers(Pizza pizzaToDecorate) {
        super(pizzaToDecorate);
    }

    @Override
    public String getDescription() {
        return super.getDescription() + ", Roasted Peppers";
    }

    @Override
    public double getCost() {
        return super.getCost() + 0.50;
    }
}

public class SpicySalami extends PizzaDecorator {
    public SpicySalami(Pizza pizzaToDecorate) {
        super(pizzaToDecorate);
    }

    @Override
    public String getDescription() {
        return super.getDescription() + ", Spicy Salami";
    }

    @Override
    public double getCost() {
        return super.getCost() + 0.70;
    }
}
//用户可以动态的添加和删除配料
public class Main {
    public static void main(String[] args) {
        Pizza pizza = new Margherita();
        System.out.println(pizza.getDescription() + ": " + pizza.getCost());

        pizza = new RoastedPeppers(pizza);
        System.out.println(pizza.getDescription() + ": " + pizza.getCost());

        pizza = new SpicySalami(pizza);
        System.out.println(pizza.getDescription() + ": " + pizza.getCost());
    }
}

行为型模式:

一、模板方法模式:

模板方法模式:模板方法模式是指定义一个算法框架,将具体内容延迟到子类去实现。

优点:
提高代码复用性:将相同部分的代码放在抽象的父类中,而将不同的代码放入不同的子类中;
实现了反向控制:通过一个父类调用其子类的操作,通过对子类的具体实现扩展不同的行为,实现了反向控制并且符合开闭原则。

例子:

喝茶:烧水----放入茶叶—喝茶。放入的茶叶每个人自己的喜好不一样,有的是普洱、有的是铁观音等。
每日工作:上班打卡----工作—下班打卡。每个人工作的内容不一样,后端开发的、前端开发、测试、产品每个人的工作内容不一样。

// 抽象基类,提供模板方法
public abstract class ReportGenerator {
    
    public final void generateReport() {
        collectData();
        formatData();
        outputReport();
    }

    protected abstract void collectData();

    protected abstract void formatData();

    protected void outputReport() {
        System.out.println("Report output complete.");
    }
}

// HTML 报告生成器
public class HTMLReportGenerator extends ReportGenerator {

    //方法重写
    protected void collectData() {
        System.out.println("Collecting data for HTML report...");
    }

    protected void formatData() {
        System.out.println("Formatting data for HTML report...");
    }
}

// PDF 报告生成器
public class PDFReportGenerator extends ReportGenerator {
	//方法重写
    protected void collectData() {
        System.out.println("Collecting data for PDF report...");
    }

    protected void formatData() {
        System.out.println("Formatting data for PDF report...");
    }
}

// 客户端代码
public class Main {
    public static void main(String[] args) {
        ReportGenerator htmlReportGenerator = new HTMLReportGenerator();
        htmlReportGenerator.generateReport();

        ReportGenerator pdfReportGenerator = new PDFReportGenerator();
        pdfReportGenerator.generateReport();
    }
}

二、责任链模式:

责任链模式:其将链中每一个节点看作是一个对象,每个节点处理的请求均不同,且内部自动维护一个下一节点对象。当一个请求从链式的首端发出时,会沿着链的路径依次传递给每一个节点对象,直至有对象处理这个请求为止。

优点:
解耦了请求与处理;请求处理者(节点对象)只需关注自己感兴趣的请求进行处理即可,对于不感兴趣的请求,直接转发给下一级节点对象;具备链式传递处理请求功能,请求发送者无需知晓链路结构,只需等待请求处理结果;链路结构灵活,可以通过改变链路结构动态地新增或删减责任;易于扩展新的请求处理类(节点),符合 开闭原则;

缺点:
责任链路过长时,可能对请求传递处理效率有影响;如果节点对象存在循环引用时,会造成死循环,导致系统崩溃;

生活案列:我们在公司内部发起一个OA审批流程,项目经理审批、部门经理审批。老板审批、人力审批。这就是生活中的责任链模式,每个角色的责任是不同。
项目案例:SpringMVC中的拦截器和Mybatis中的插件机制,都是拦截器经典实现。

// 步骤1:创建抽象类。
public abstract class Handler {
   protected Handler successor;

   public void setSuccessor(Handler successor){
      this.successor = successor;
   }

   public abstract void handleRequest(int request);
}

// 步骤2:创建具体的处理类,继承抽象处理器。
public class ConcreteHandler1 extends Handler {

   public void handleRequest(int request) {
      if(request >= 0 && request < 10) {
         System.out.println(this.getClass().getName() + " 处理请求 " + request);
      } else {
         successor.handleRequest(request);
      }
   }
}

public class ConcreteHandler2 extends Handler {

   public void handleRequest(int request) {
      if(request >= 10 && request < 20) {
         System.out.println(this.getClass().getName() + " 处理请求 " + request);
      } else {
         successor.handleRequest(request);
      }
   }
}

// 步骤3:创建接收者链。
public class Client {
   public static void main(String[] args) {
      Handler h1 = new ConcreteHandler1();
      Handler h2 = new ConcreteHandler2();
      
      h1.setSuccessor(h2);//形成责任链

      // send requests to the chain
      for(int i = 0; i < 30; i += 5) {
         h1.handleRequest(i);
      }
   }
}

三、观察者模式:

观察者模式是定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。
优点:
观察者模式可以实现表示层和数据逻辑层的分离,并定义了稳定的消息更新传递机制,抽象了更新接口,使得可以有各种各样不同的表示层作为具体观察者角色;观察者模式在观察目标和观察者之间建立一个抽象的耦合;观察者模式支持广播通信;观察者模式符合开闭原则(对拓展开放,对修改关闭)的要求。

在Spring中大量的使用的观察者模式,只要看到是以Event结尾或者Publish开头的基本上都是观察者模式。

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

// 步骤1:创建Subject类
class Subject {
  private List<Observer> observers = new ArrayList<Observer>();
  private int state;

  public int getState() {
    return state;
  }

  public void setState(int state) {
    this.state = state;
    notifyAllObservers();
  }

  public void attach(Observer observer){
    observers.add(observer);      
  }

  public void notifyAllObservers(){
    for (Observer observer : observers) {
      observer.update();
    }
  }     
}

// 步骤2:创建Observer类。
abstract class Observer {
  protected Subject subject;
  public abstract void update();
}

// 步骤3:创建具体的观察者类。
class BinaryObserver extends Observer {
  public BinaryObserver(Subject subject){
    this.subject = subject;
    this.subject.attach(this);
  }

  @Override
  public void update() {
    System.out.println( "Binary String: " + Integer.toBinaryString( subject.getState() ) ); 
  }
}

class OctalObserver extends Observer {
  public OctalObserver(Subject subject){
    this.subject = subject;
    this.subject.attach(this);
  }

  @Override
  public void update() {
    System.out.println( "Octal String: " + Integer.toOctalString( subject.getState() ) ); 
  }
}

// 步骤4:使用Subject和具体的观察者对象。
public class ObserverPatternDemo {
  public static void main(String[] args) {
    Subject subject = new Subject();

    new OctalObserver(subject);
    new BinaryObserver(subject);

    System.out.println("First state change: 15");  
    subject.setState(15);
    System.out.println("Second state change: 10");  
    subject.setState(10);
  }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值