当前位置: 首页 > article >正文

Java常见设计模式(上):创建型模式

设计模式概述

设计模式是从大量的实践中总结和理论化之后优选的代码结构编程风格、以及问题解决思路。它们如同经典棋谱,不同的棋局不同棋局对应不同“套路”,帮助我们高效应对各种编程挑战。

设计原则是一些通用的设计指导方针,它们提供了如何设计一个优秀的软件系统的基本思想和规则。指导着设计者如何组织代码以实现高内聚、低耦合、易扩展易维护的软件系统。

  • 设计模式则是在特定情况下解决常见问题的经验性解决方案,它们提供了如何实现这些设计原则的具体方法。
  • 设计模式往往是在满足设计原则的基础上被应用的。设计模式可以看作是实现设计原则的一种具体方式。

六大设计原则

  • 单一职责原则:一个类应该只有一个引起它变化的原因。换句话说,一个类应该只有一项职责。确保类内聚性,降低耦合性。
  • 开闭原则:软件实体(如类、模块、函数)应对扩展开放,对修改关闭,优先通过扩展现有代码实现新功能,而非修改已有代码。
  • 里氏替换原则:子类应能替换父类而不影响程序正确性。子类只能扩展父类功能,不能改变已有行为。所有引用父类的地方,必须能使用子类的对象。
  • 接口隔离原则:客户端不应该依赖于它不需要的接口。一个类应该只提供它需要的接口,而不应该强迫客户端依赖于它不需要的接口。
  • 依赖倒置原则:高层模块不应依赖低层模块,二者应依赖于抽象。抽象不应依赖具体实现,具体实现应依赖抽象。
  • 迪米特法则:一个对象应该对其他对象保持最少的了解。换句话说,一个对象只应该与它直接相互作用的对象发生交互,而不应该与其它任何对象发生直接的交互。这样可以降低类之间的耦合性,提高系统的灵活性和可维护性。
    在这里插入图片描述

设计模式分类

设计模式主要有以下三种:

  • 创建型模式,解决对象创建问题。共5种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
  • 结构型模式,主要解决对象组合问题。共7种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
  • 行为型模式,主要解决对象之间的交互问题。共11种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

5种创建型模式详解

创建型模式关注对象的创建与组装,通过抽象化和解耦对象的创建过程,以提高系统灵活性与可扩展性。

单例(Singleton)设计模式

单例模式确保一个类只有一个实例,并提供全局访问点。

实现思路:如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造器的访问权限设置为private,这样,就不能用new操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的。
优点:
降低系统性能开销,特别适用于资源消耗大的对象(如配置、数据库连接等)。

应用场景
全局资源管理(如配置文件、日志记录器、数据库连接池等)。
➢网站的计数器,一般也是单例模式实现,否则难以同步。
➢应用程序的日志应用,一般都使用单例模式实现,这一般是由于共享的日志文件一 直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
➢数据库连接池的设计一般也是采用单例模式,如果需要频繁地与数据库交互,使用单例模式可以确保只有一个数据库连接实例,从而减少数据库连接等数量,提高运用程序的性能。
➢项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置文件数据,都生成一个对象去读取。
➢Application也是单例的典型应用
➢Windows的Task Manager (任务管理器)就是很典型的单例模式
➢Windows的Recycle Bin (回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。

代码实现
饿汉式

- 类初始化时直接创建单例对象,而类初始化过程是没有线程安全问题的
class Bank{
    //1.私有化类的构造器
    private Bank(){}
    //2.内部创建类的对象
    //4.要求此对象也必须声明为静态的
    private static Bank instance = new Bank();
    //3.提供公共的静态的方法,返回类的对象
    public static Bank getInstance(){
        return instance;
    }
}

懒汉式

-- 延迟创建对象,第一次调用getinstance方法再创建对象
class Order{
    //1.私有化类的构造器
    private Order(){
    }
    
    //2.声明当前类对象,没有初始化
    //4.此对象也必须声明为static的
    private static Order instance = null;
    //3.声明public、static的返回当前类对象的方法
    public static Order getInstance( ){
        if(instance == null){
            instance = new Order();
        }
        return instance;
     }
}

饿汉式VS懒汉式
饿汉式:

  • 坏处:对象加载时间过长。
  • 好处:饿汉式是线程安全的
    懒汉式:
  • 好处:延迟对象的创建。
  • 目前的写法坏处:线程不安全。—>到多线程内容时,再修改

懒汉式-线程安全版

class Order{
    private Order(){
    }
    
    private static Order instance = null;
    
    //同步代码块
    public static  Order getInstance( ){
        //方式一:效率稍差
        synchronized(Order.class){
            if(instance == null){
                instance = new Order();
            }
            return instance;
        }
        
        //方式二:效率稍高  当实例没有创建时需要加锁。双重检查锁
        if (instance == null){
             synchronized(Order.class){
                if(instance == null){
                    instance = new Order();
                }
            return instance;
        }        
     }
     
     
    //方法三:同步方法(锁粒度大,比较粗糙)
    public static synchronized Order getInstance( ){
        if(instance == null){
           instance = new Order();
        }
        return instance;
     }
}

工厂模式

工厂模式通过提供一个接口来创建对象,隐藏具体实现细节。
应用场景
1.对象的创建过程比较复杂,需要进行封装:如果创建一个对象需要进行复杂的初始化过程,或者需要从多个地方获取数据才能创建对象,那么使用工厂模式可以将这些过程封装起来,让客户端代码更加简洁和易于理解。
2.需要动态扩展或修改对象的创建过程:如果需要增加或修改某个对象的创建过程,而又不希望对客户端代码产生影响,那么使用工厂模式可以很方便地实现这个需求。
3.需要统一管理对象的创建:如果需要统一管理对象的创建过程,或者需要对创建的对象进行某些统一的处理,那么使用工厂模式可以很好地实现这个需求。
4.需要根据不同的条件创建不同的对象:如果需要根据不同的条件来创建不同类型的对象,那么使用工厂模式可以很方便地实现这个需求。

代码实现
通过一个工厂类来封装对象的创建过程,客户端只需要告诉工厂类需要创建哪种类型的对象即可。将对象的创建过程与客户端代码分离开来,使代码更加灵活和易于扩展

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

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

// 具体产品类B
public class ConcreteProductB implements Product {
    @Override
    public void operation() {
        System.out.println("ConcreteProductB operation.");
    }
}

// 工厂类
public class SimpleFactory {
    public static Product createProduct(String type) {
        if ("A".equals(type)) {
            return new ConcreteProductA();
        } else if ("B".equals(type)) {
            return new ConcreteProductB();
        } else {
            throw new IllegalArgumentException("Invalid product type.");
        }
    }
}

客户端可以通过调用

SimpleFactory.createProduct方法来创建不同类型的产品对象
Product productA = SimpleFactory.createProduct("A");
productA.operation(); // 输出 "ConcreteProductA operation."

Product productB = SimpleFactory.createProduct("B");
productB.operation(); // 输出 "ConcreteProductB operation."

使用小结
  在Java中,工厂模式广泛应用于各种框架和类库中,例如JDBC中的DataSource工厂、Spring框架中的Bean工厂、MyBatis框架中的SqlSessionFactory等等。

抽象工厂模式 Abstract factoryPattern

提供一个接口,用于创建相关或依赖对象的序列,而不需要指定实际实现类。
在Java中,抽象工厂模式通常包括以下几个角色:

  • AbstractFactory:抽象工厂,声明了创建产品对象的方法。
  • ConcreteFactory:具体工厂,实现了创建产品对象的方法。
  • AbstractProduct:抽象产品,声明了产品对象的共性接口。
  • Product:具体产品,实现了抽象产品中的抽象方法,构成产品族。
  • Client:客户端,通过调用工厂类的方法创建产品对象。
      抽象工厂和工厂模式都是创建对象的设计模式,它们的主要区别什么呢?
  • 目的不同:工厂模式用于创建一类产品对象的实例,而抽象工厂模式用于创建一组相关的产品对象实例。
  • 实现方式不同:工厂模式中只有一个工厂类,该类负责创建所有的产品对象;而抽象工厂模式中有多个工厂类,每个工厂类负责创建一组相关的产品对象。
  • 范围不同:工厂模式通常用于创建单个对象,而抽象工厂模式通常用于创建一组相关的对象

使用场景

  • 当需要创建一组相关的产品对象,这些产品对象之间有共同的约束条件,需要一起使用时,可以使用抽象工厂模式。
  • 当系统需要独立于产品的创建,组装和表示时,可以使用抽象工厂模式
  • 当系统需要支持多个不同的产品族,且不希望依赖于具体产品类时,可以使用抽象工厂模式。
  • 当系统需要在运行时切换不同的产品族时,可以使用抽象工厂模式。
  • 当需要遵循“开闭原则”,即系统需要扩展新的产品族时,不需要修改已有代码,可以使用抽象工厂模式。

代码实现
假设有一个在线商店需要提供两种支付方式:信用卡支付和网银支付,每种支付方式又包含两种具体的实现:Visa 信用卡支付和 MasterCard 信用卡支付,以及支付宝网银支付和微信网银支付。
首先,定义支付方式的接口:

// 信用卡支付接口
public interface CreditCardPayment {
    void pay(double amount);
}

// 网银支付接口
public interface OnlineBankingPayment {
    void pay(double amount);
}

接着,定义具体的实现:

// Visa 信用卡支付
public class VisaCreditCardPayment implements CreditCardPayment {
    @Override
    public void pay(double amount) {
        System.out.println("Visa credit card payment: $" + amount);
    }
}

// MasterCard 信用卡支付
public class MasterCardCreditCardPayment implements CreditCardPayment {
    @Override
    public void pay(double amount) {
        System.out.println("MasterCard credit card payment: $" + amount);
    }
}

// 支付宝网银支付
public class AlipayOnlineBankingPayment implements OnlineBankingPayment {
    @Override
    public void pay(double amount) {
        System.out.println("Alipay online banking payment: ¥" + amount);
    }
}

// 微信网银支付
public class WeChatOnlineBankingPayment implements OnlineBankingPayment {
    @Override
    public void pay(double amount) {
        System.out.println("WeChat online banking payment: ¥" + amount);
    }
}

然后,定义抽象工厂类

// 抽象支付工厂
public abstract class PaymentFactory {
    public abstract CreditCardPayment createCreditCardPayment();
    public abstract OnlineBankingPayment createOnlineBankingPayment();
}

定义具体的支付工厂类:

// Visa 信用卡支付工厂
public class VisaPaymentFactory extends PaymentFactory {
    @Override
    public CreditCardPayment createCreditCardPayment() {
        return new VisaCreditCardPayment();
    }

    @Override
    public OnlineBankingPayment createOnlineBankingPayment() {
        return new AlipayOnlineBankingPayment();
    }
}

// MasterCard 信用卡支付工厂
public class MasterCardPaymentFactory extends PaymentFactory {
    @Override
    public CreditCardPayment createCreditCardPayment() {
        return new MasterCardCreditCardPayment();
    }

    @Override
    public OnlineBankingPayment createOnlineBankingPayment() {
        return new WeChatOnlineBankingPayment();
    }
}

最后,使用抽象工厂来创建支付对象

public class Client {
    public static void main(String[] args) {
        // 选择 Visa 信用卡支付工厂
        PaymentFactory paymentFactory = new VisaPaymentFactory();

        // 创建 Visa 信用卡支付对象
        CreditCardPayment creditCardPayment = paymentFactory.createCreditCardPayment();
        creditCardPayment.pay(100);

        // 创建支付宝网银支付对象
        OnlineBankingPayment onlineBankingPayment = paymentFactory.createOnlineBankingPayment();
        onlineBankingPayment.pay(200);

        // 选择 MasterCard 信用卡支付工厂
        PaymentFactory paymentFactory2 = new MasterCardPaymentFactory();

        // 创建  MasterCard 信用卡支付对象
        CreditCardPayment creditCardPayment2 = paymentFactory2.createCreditCardPayment();
        creditCardPayment2.pay(100);

        // 创建微信网银支付对象
        OnlineBankingPayment onlineBankingPayment2 = paymentFactory2.createOnlineBankingPayment();
        onlineBankingPayment2.pay(200);        
}

使用小结
  Java 抽象工厂模式在很多框架和应用程序中都有广泛的应用。以下是一些具体的应用:
1.Java 数据库连接框架 JDBC 中,使用抽象工厂模式来创建连接对象,例如 Connection、Statement 等。
2.Java 中的 XML 处理器 DOM 和 SAX,也使用抽象工厂模式来创建解析器和生成器对象。
3.Java 中的 Java Cryptography Architecture (JCA) 也使用抽象工厂模式,用于创建加密算法和密钥生成器对象。
总之,Java 抽象工厂模式可以在许多应用程序和框架中找到,它可以帮助您更好地组织和管理代码,提高代码的可扩展性和灵活性。

建造者模式

将复杂对象的创建与表示分离,使得同样的构建过程可以创建不同的表示。

原型模式 Prototype Pattern

通过克隆来创建对象,避免了通过 new 关键字显示调用构造函数的开销。

使用场景

  • 当对象创建的过程比较耗时或者比较复杂,例如需要进行复杂的计算或者涉及到网络请求等操作,可以使用原型模式来避免重复的初始化过程。
  • 当需要创建的对象需要和其他对象进行协同工作时,例如需要创建一个包含多个对象的组合对象,可以使用原型模式来复制一个已有的组合对象,然后进行修改来创建新的组合对象。
  • 当需要动态地增加或者删除一些对象时,可以使用原型模式来复制一个已有的对象,然后进行修改来创建新的对象。
  • 当需要保护对象的复杂状态时,例如当一个对象的创建需要大量的数据初始化时,可以使用原型模式来保护这些数据,避免因为对象的复制而产生意外的副作用。

代码实现

// 定义一个原型接口
interface Prototype {
    public Prototype clone();
}

// 具体的原型类
class ConcretePrototype implements Prototype {
    public Prototype clone() {
        return new ConcretePrototype();
    }
}

// 客户端代码
class Client {
    public static void main(String[] args) {
        Prototype prototype = new ConcretePrototype();
        Prototype clone = prototype.clone();
    }
}

使用小结
1.Java中的Object类实现了Cloneable接口,这就意味着Java中的任何对象都可以实现原型模式。通过实现Cloneable接口,并重写Object类中的clone()方法,可以实现原型模式。例如 ArrayList、HashMap 等集合类都实现了Cloneable 接口,可以通过复制现有对象来创建新的对象。
2.Java中的线程池也是使用了原型模式,线程池中的每个线程都是从原型线程中复制而来,而不是每次创建新的线程。


http://www.kler.cn/a/563036.html

相关文章:

  • 介绍一下自动驾驶 泊车算法
  • Agilent83630B信号发生器,可提供用来满足各种应用需要的机型选择机会
  • 【医学分割】基于显式形状先验学习的医学图像分割
  • Android内存优化指南:从数据结构到5R法则的全面策略
  • PCL源码分析:曲面法向量采样
  • RK3568平台开发系列讲解(内核篇)Linux 内核启动流程
  • 观成科技:海莲花“PerfSpyRAT”木马加密通信分析
  • 微服务架构与传统的单体架构有什么区别?微服务架构(Spring Cloud + Maven)强在哪?
  • LeetCode 1472.设计浏览器历史记录:一个数组完成模拟,单次操作均O(1)
  • css实现左右切换平滑效果
  • 数字人AIGC实训室:以AI技术赋能数字化教育
  • python基础学习day01
  • 高效能计算与高速数据传输的完美结合:飞腾D2000处理器与复旦微双FPGA集成主板
  • 数据库二三事(8)
  • 数组的使用
  • HTTPS 与 HTTP 的区别在哪?
  • DeepSeek开源周Day1:FlashMLA引爆AI推理性能革命!
  • PyTorch 源码学习:GPU 内存管理之它山之石——TensorFlow BFC 算法
  • python爬虫学习第十一篇爬取指定类型数据
  • Qt Creator + CMake 构建教程