JAVA:深入了解JAVA中的23种设计模式(一)- 创建型模式

一、前言

  Java中的23种设计模式并不是Java语言本身所特有的,而是从面向对象编程的设计原则和经验中总结出来的。这些设计模式是在大量的实践经验和代码重构中提炼出来的,旨在解决软件开发中反复出现的问题,提高代码的可维护性、可重用性和可扩展性。

  只有更好地掌握了设计模式,我们的代码编写才能更规范、简洁,效率更高。接下来,我们一起来看看这些计模式的使用场景。

二、设计模式的作用

解决常见问题:在软件开发过程中,经常会遇到一些重复出现的问题,如对象的创建、对象之间的交互、系统的扩展等。设计模式就是对这些常见问题的解决方案的总结,它们提供了一种经过验证的、可复用的解决方案,使得开发人员可以更加高效地解决这些问题。

提高代码质量:设计模式遵循面向对象编程的原则,如单一职责原则、开闭原则、里氏替换原则等,这些原则有助于提高代码的质量。通过使用设计模式,可以使得代码更加清晰、简洁、易于理解和维护。

提高可维护性:设计模式通过降低代码的耦合度和提高内聚度,使得代码更加易于维护和修改。例如,工厂方法模式和抽象工厂模式通过封装对象的创建过程,降低了代码与具体实现之间的耦合度,使得代码更加易于修改和扩展。

提高可扩展性:设计模式通过定义清晰的接口和类之间的关系,使得系统更加易于扩展。例如,适配器模式可以通过将已有的接口转换为新的接口,使得原本不兼容的类可以一起工作,从而提高了系统的可扩展性。

提高可重用性:设计模式提供了一种可复用的解决方案,使得开发人员可以更加高效地重用已有的代码和经验。通过使用设计模式,可以避免重复造轮子,提高开发效率。

三、设计模式的分类

设计模式分为三大类:

创建型模式:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

结构型模式:适配器模式、装饰模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

行为型模式:策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

四、创建型模式

1. 单例模式
1.1 简介
  它确保一个类只有一个实例,并提供一个全局访问点。在很多情况下,一个类只需要一个实例就足够了,例如配置管理类、数据库连接类、线程池类等。使用单例模式可以避免因多次创建实例而导致的资源浪费,以及因多个实例操作共享资源而导致的数据不一致问题。

1.2 使用场景
  数据库连接池:在应用中,数据库连接池只需要一个实例,用于管理和复用数据库连接,避免频繁创建和销毁连接。

  配置文件读取:应用配置通常只需要读取一次,并存储在内存中供后续使用,此时可以使用单例模式来管理配置对象。

  线程池:线程池用于管理和复用线程,避免频繁创建和销毁线程,以提高系统性能。

1.3 代码示例

public class Singleton {  
    // 声明一个静态的Singleton实例,并默认初始化为null  
    private static Singleton instance = null;  
  
    // 私有构造方法,防止外部通过new Singleton()创建实例  
    private Singleton() {}  
  
    // 提供一个全局的访问点来获取Singleton实例  
    public static Singleton getInstance() {  
        if (instance == null) {  
            // 在第一次调用时创建实例  
            instance = new Singleton();  
        }  
        // 返回实例  
        return instance;  
    }  
  
    // 其他方法...  
}

  上面的代码示例虽然可以实现单例模式,但在多线程环境下可能存在线程安全问题。为了解决这个问题,可以使用双重检查锁定(Double-Check Locking)或静态内部类等方式来实现线程安全的单例模式。

public class Singleton {  
    // volatile关键字确保instance在多线程环境下可见  
    private volatile static Singleton instance = null;  
  
    private Singleton() {}  
  
    public static Singleton getInstance() {  
        if (instance == null) {  
            synchronized (Singleton.class) {  
                if (instance == null) {  
                    instance = new Singleton();  
                }  
            }  
        }  
        return instance;  
    }  
  
    // 其他方法...  
}

volatile关键字确保instance在多线程环境下的可见性,而双重检查锁定则确保在多个线程同时请求实例时,只有一个线程会创建实例

2. 工厂方法模式
2.1 简介
  一种方式来封装对象的创建,而不需要在代码中直接使用new关键字来创建对象实例。工厂方法模式定义了一个用于创建对象的接口,但将实际创建工作延迟到子类中。这样,调用者只需要知道它所需要的抽象产品类型,而无需关心具体是哪个具体产品类。

2.2 使用场景
  连接数据库:降低对象的产生和销毁。

  系统需要引入新模块时不对现有系统产生较大影响:当需要添加新产品时,只需要添加新的具体产品类和对应的工厂子类,而不需要修改现有的客户端代码。

2.3 代码示例

public abstract class Product {  
    public abstract void use();  
}  
  
// 产品类A  
public class ConcreteProductA extends Product {  
    @Override  
    public void use() {  
        System.out.println("Using Product A");  
    }  
}  
  
// 产品类B  
public class ConcreteProductB extends Product {  
    @Override  
    public void use() {  
        System.out.println("Using Product B");  
    }  
}  
  
// 抽象工厂类  
public abstract class Creator {  
    public abstract Product createProduct();  
}  
  
// 工厂类A,负责创建ProductA的实例  
public class ConcreteCreatorA extends Creator {  
    @Override  
    public Product createProduct() {  
        return new ConcreteProductA();  
    }  
}  
  
// 工厂类B,负责创建ProductB的实例  
public class ConcreteCreatorB extends Creator {  
    @Override  
    public Product createProduct() {  
        return new ConcreteProductB();  
    }  
}  

3. 抽象工厂模式
3.1 简介
  创建一组相关或相互依赖的对象提供一个接口,而且无需指定它们的具体类。

3.2 使用场景
  系统需要独立于它的产品的创建、组合和表示:当你需要生成产品族,而产品族中一组产品是一起使用时,可以使用抽象工厂模式。

  系统需要配置不同的产品配置:一个系统需要多种产品类,这些产品类有很多公共的接口,但系统运行时需要动态决定使用哪一种产品类。

  强调一系列相关的产品对象一起使用,以形成统一的配置风格:在GUI系统中,一个界面通常会有一组按钮(Button)、文本框(TextBox)、列表框(ListBox)等,这些对象通常是作为一个整体来使用的,可以使用抽象工厂模式来创建这些对象。

  系统需要增加新的产品对象时,不修改现有代码:当系统需要新的产品对象时,只需要增加新的具体产品类和对应的具体工厂类,不需要修改现有的客户端代码。

3.3 代码示例

// 抽象产品接口  
public interface A {  
    void display();  
}  
  
// 抽象产品接口  
public interface TextBox {  
    void display();  
}  
  
// 具体产品类A  
public class ConcreteButtonA implements Button {  
    @Override  
    public void display() {  
        System.out.println("Displaying Button A");  
    }  
}  
  
// 具体产品类A  
public class ConcreteTextBoxA implements TextBox {  
    @Override  
    public void display() {  
        System.out.println("Displaying TextBox A");  
    }  
}  
  
// 具体产品类B  
public class ConcreteButtonB implements Button {  
    @Override  
    public void display() {  
        System.out.println("Displaying Button B");  
    }  
}  
  
// 具体产品类B  
public class ConcreteTextBoxB implements TextBox {  
    @Override  
    public void display() {  
        System.out.println("Displaying TextBox B");  
    }  
}  
  
// 抽象工厂接口  
public interface GUIFactory {  
    Button createButton();  
    TextBox createTextBox();  
}  
  
// 具体工厂类A  
public class ConcreteFactoryA implements GUIFactory {  
    @Override  
    public Button createButton() {  
        return new ConcreteButtonA();  
    }  
  
    @Override  
    public TextBox createTextBox() {  
        return new ConcreteTextBoxA();  
    }  
}  
  
// 具体工厂类B  
public class ConcreteFactoryB implements GUIFactory {  
    @Override  
    public Button createButton() {  
        return new ConcreteButtonB();  
    }  
  
    @Override  
    public TextBox createTextBox() {  
        return new ConcreteTextBoxB();  
    }  
}  

4. 建造者模式
4.1 简介
  它可以将一个复杂对象的构建过程与其表示相分离,使得同样的构建过程可以创建出不同的表示。

建造者模式通常包含以下几个角色:

Product(产品角色):一个具体的复杂对象。

Builder(抽象建造者):为创建一个Product对象的各个部件指定抽象接口。

ConcreteBuilder(具体建造者):实现Builder接口,构建和装配该产品的各个部件;定义并明确它所创建的表示;并提供一个检索产品的接口。

Director(指挥者):负责安排已有模块的顺序,然后告诉Builder开始建造。

4.2 使用场景
  对象的构建过程复杂:当构建对象需要多个步骤,并且这些步骤可能会因构建的不同表示而有所不同时。

  相同的构建过程,不同的表示:当对象的构建过程相同,但希望创建的对象具有不同的表示时。

  构建对象过程中需要动态地添加属性:在某些场景下,可能需要动态地添加或删除对象的某些属性,而不需要修改已经存在的代码。

4.3 代码示例

public class Computer {  
    private String cpu;  
    private String ram; 
}  
  
// 抽象建造者  
public interface ComputerBuilder {  
    void setCpu(String cpu);  
    void setRam(String ram);  
    void setHdd(String hdd);  
    Computer build();  
}  
  
// 具体建造者  
public class ComputerBuilderImpl implements ComputerBuilder {  
    private Computer computer = new Computer();  
  
    @Override  
    public void setCpu(String cpu) {  
        computer.setCpu(cpu);  
    }  
  
    @Override  
    public void setRam(String ram) {  
        computer.setRam(ram);  
    }  
  
    @Override  
    public void setHdd(String hdd) {  
        computer.setHdd(hdd);  
    }  
  
    @Override  
    public Computer build() {  
        return computer;  
    }  
}  
  
// 指挥者  
public class ComputerDirector {  
    private ComputerBuilder builder;  
  
    public ComputerDirector(ComputerBuilder builder) {  
        this.builder = builder;  
    }  
  
    public void constructComputer() {  
        builder.setCpu("Intel Core i7");  
        builder.setRam("16GB DDR4");  
        builder.setHdd("1TB SSD");  
    }  
  
    public Computer getComputer() {  
        return builder.build();  
    }  
}  

5.原型模式
5.1 简介
  通过将一个原型对象传给那个要发动创建的对象,这个对象通过请求原型对象拷贝它们自己来实施创建。

5.2 使用场景

  性能考虑:当直接创建对象的代价比较大时,例如一个对象需要在一个高代价的数据库操作之后被创建,我们可以采用原型模式,通过拷贝原型对象来避免重复的高代价操作。

  资源消耗:当类初始化需要消耗大量的资源(如数据、硬件资源等)时,原型模式可以通过拷贝已有的对象来避免这些资源的重复消耗。

  保护性拷贝:当一个对象需要提供给其他对象访问,并且各个调用者可能需要修改其值时,可以考虑使用原型模式来拷贝多个对象供调用者使用,这样可以避免对原始对象的直接修改。

5.3 代码示例

// 抽象原型类  
public interface Prototype {  
    public Prototype clone();  
}  
  
// 具体原型类  
public class RealPrototype implements Prototype {  
    private String data;  
  
    public RealPrototype(String data) {  
        this.data = data;  
    }  
  
    public String getData() {  
        return data;  
    }  
  
    // 实现clone方法  
    @Override  
    public Prototype clone() {  
        RealPrototype prototype = null;  
        try {  
            prototype = (RealPrototype) super.clone();  
        } catch (CloneNotSupportedException e) {  
            e.printStackTrace();  
        }  
        return prototype;  
    }  
}  

  以上是创建型模式中的设计模式简介与应用场景,下一篇将介绍结构型模式下的设计模式的应用场景以及代码示例展示。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值