Java设计模式——创建型模式(二)

本文深入探讨了Java中的四种创建型设计模式:简单工厂模式、工厂方法模式、抽象工厂模式和建造者模式。简单工厂模式通过静态工厂方法创建对象;工厂方法模式则将产品创建的责任交给子类;抽象工厂模式允许创建产品族的实例;建造者模式用于构建复杂对象,通过逐步组装过程。这些模式提供了更灵活的创建对象的方式,便于系统扩展和维护。
摘要由CSDN通过智能技术生成

创建型模式

创建型模式的作用就是创建对象,说到创建一个对象,最熟悉的就是new一个对象,然后set相关属性。但是在很多场景下,我们需要给客户端提供更加友好的创建对象的方式,尤其是那种我们定义了类,但是需要提供给其他开发者使用的时候。

1、简单工厂模式

定义:定义一个工厂类,可以根据参数的不同返回不同类型的实例,被创建的实例都具有共同的父类

在简单工厂模式中用于被创建实例的方法通常为静态(static)方法,因此简单工厂模式又被称为静态工厂方法(Static Factory Method)。下面进行代码理解:

代码示例:
package com.creation_Pattern.p01_simple_factory;
//产品
public interface Product {
    //价格
    int getPrice();

    //产品名
    String getName();
}
代码示例:
package com.creation_Pattern.p01_simple_factory;
//产品A
public class ProductA implements Product{
    @Override
    public int getPrice() {
        return 200;
    }

    @Override
    public String getName() {
        return "Product_A";
    }
}
代码示例:
package com.creation_Pattern.p01_simple_factory;
//产品B
public class ProductB implements Product{
    @Override
    public int getPrice() {
        return 200;
    }

    @Override
    public String getName() {
        return "Product_B";
    }
}

按照正常创建对象我们会new ProductA,实现其方法,new ProductB,实现其方法,但是当我们建造一个工厂类

示例代码:
package com.creation_Pattern.p01_simple_factory;
/**
 * @author: 皮先生kx
 * @Description: 根据输入类型生成对应产品
 * @date: created on 2021/10/17 16:24
 * @email: 2472177078@qq.com
 */
public class Factory {
    public static Product createProduct(String type){
        Product product = null;

        switch (type){

            case "A":
                product = new ProductA();
                break;

            case "B":
                product = new ProductB();
                break;
        }

        return product;
    }
}

代码示例:
package com.creation_Pattern.p01_simple_factory;
public class Test {
    public static void main(String[] args) {
        //ProductA productA = new ProductA(); 
		
        Product productA = Factory.createProduct("A");
        System.out.println(productA.getName()+", "+productA.getPrice());

    }
}

简单工厂模式的优点:

  • 工厂类包含必要的逻辑判断,可以决定在什么时候创建哪一个产品的实例。客户端可以免除直接创建产品对象的职责。
  • 客户端无需知道所创建具体产品的类名,只需要知道参数即可
  • 也可以引入配置文件,在不修改客户端代码的情况下更好和添加新的具体产品类

简单工厂模式的缺点:

  • 工厂类集中了所有产品的创建逻辑,职责过重,一旦异常,整个系统将受到影响
  • 使用简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理解难度
  • 系统扩展很难,一旦增加新产品就必须修改工厂逻辑,在产品类型较多时,可能造成逻辑过于复杂
  • 简单工厂模式使用了static工厂方法,造成工厂角色无法形成基于继承的登记结构

简单工厂模式的使用场景:

  • 工厂类复杂创建的对象较少,因为不会造成工厂方法中的业务逻辑过于发杂
  • 客户端只知道传入工厂类的参数,对如何创建对象不关心
2、工厂方法模式

定义一个抽象工厂,其定义了产品的生产接口,但是不负责具体的产品,将生产任务交给不同的派生类工厂。这样工厂就不用通过指定类型来创建对象了。

代码示例:
package com.creation_Pattern.p02_factory_method;
//定义工厂接口
public interface IFactory {
    Product createProduct();
}
代码示例:
package com.creation_Pattern.p02_factory_method;
//由FactoryA来实现工厂接口用于A产品的专项生产
public class FactoryA implements IFactory{
    @Override
    public Product createProduct() {
        return new ProductA();
    }
}
代码示例:
package com.creation_Pattern.p02_factory_method;
//由FactoryB来实现工厂接口用于B产品的专项生产
public class FactoryB implements IFactory{
    @Override
    public Product createProduct() {
        return new ProductB();
    }
}
代码示例:
package com.creation_Pattern.p02_factory_method;
/**
 * @author: 皮先生kx
 * @Description:
 * @date: created on 2021/10/17 16:23
 * @email: 2472177078@qq.com
 */
public class Test {
    public static void main(String[] args) {
		//选择工厂进行生产
        FactoryA factoryA = new FactoryA();
        Product productA = factoryA.createProduct();
        
        System.out.println(productA.getName()+','+productA.getPrice());
    }
}

当我们需要增加产品C的时候我们就可以创建一个FactoryC工厂C来实现IFactory接口,对原有的代码没有任何影响,比较符合我的开放封闭原则。

3、抽象工厂模式

对于上面两种模式不管工厂怎么拆分抽象,都只是针对一类产品,如果要生产另外一种产品,应该怎么表示呢?

最简单的方式是把工厂方式模式完全复制一份,用于生产新的产品。显然这是一个笨办法,并不利于扩展和维护。

代码示例:
package com.creation_Pattern.p03_abstract_factory;
//礼品接口
public interface Gift {

    String getGiftName();
}

代码示例:
package com.creation_Pattern.p03_abstract_factory;
//礼品A
public class GiftA implements Gift{
    @Override
    public String getGiftName() {
        return "Gift_A";
    }
}

代码示例:
package com.creation_Pattern.p03_abstract_factory;
//礼品B
public class GiftB implements Gift{
    @Override
    public String getGiftName() {
        return "Gift_B";
    }
}

代码示例:
package com.creation_Pattern.p03_abstract_factory;

public interface IFactory {

    Product createProduct();

    Gift createGift();
}

代码示例:
package com.creation_Pattern.p03_abstract_factory;

public class FactoryA implements IFactory {
    @Override
    public Product createProduct() {
        return new ProductA();
    }

    @Override
    public Gift createGift() {
        return new GiftA();
    }
}

```java
代码示例:
package com.creation_Pattern.p03_abstract_factory;

public class FactoryB implements IFactory {
    @Override
    public Product createProduct() {
        return new ProductB();
    }

    @Override
    public Gift createGift() {
        return new GiftB();
    }
}
代码示例:
package com.creation_Pattern.p03_abstract_factory;

/**
 * @author: 皮先生kx
 * @Description:
 * @date: created on 2021/10/17 16:23
 * @email: 2472177078@qq.com
 */
public class Test {

    public static void main(String[] args) {

        FactoryA factoryA = new FactoryA();

        Product productA = factoryA.createProduct();
        Gift giftA = factoryA.createGift();
        System.out.println(productA.getName()+','+productA.getPrice());


    }
}

抽象工厂模式通过在AbstractFactory中增加创建新产品的接口,并在具体子工厂中实现新加产品的创建,当然前提是子工厂支持生产该产品。否则继承的这个接口可以什么也不干。

4、建造者模

定义:建造者模式是将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示

如何使用:用户只需要给出复杂对象的类型和内容,建造者模式复制按顺序创建复杂对象(把内部的建筑过程和细节隐藏起来)

使用场景:

  • 方便用户创建复杂的对象(不需要知道实现过程)
  • 代码复用性/封装性(将对象构建过程和细节进行复用/封装)

4.1 通过Client、Director、Builder和Product形成的建造者模式

1.角色

抽象建造者(builder):描述具体建造者的公共接口,一般用来定义建造细节的方法,并不涉及具体的对象部件的创建

具体建造者(ConcreteBuilder):描述具体建造者,并实现抽象建造者公共接口

指挥者(Director):调用具体建造者来创建复杂对象(产品)的各个部分,并按照一定顺序(流程)来创建发杂对象

产品(Product):描述一个由一系列部件组成较为复杂的对象

2.步骤
以建造房子为事例,假设造房子简化为如下步骤:地基—>钢筋工程—>铺电线---->粉刷

首先要找一个建筑公司(指挥者),建筑公司指挥工人(具体建造者)造房子(产品),最后验收:

	1)创建抽象建造者定义造房子步骤
	2)创建工人具体实现造房子步骤
	3)创建承包商指挥工人施工
	4)验收,检查是否建造完成
代码示例:
package com.creation_Pattern.p04_builder;

/**
 * @author: 皮先生kx
 * @Description: 产品房子
 * @date: created on 2021/10/17 19:18
 * @email: 2472177078@qq.com
 */
public class Product {

    private String buildA;
    private String buildB;
    private String buildC;
    private String buildD;

    public String getBuildA() {
        return buildA;
    }
    public void setBuildA(String buildA) {
        this.buildA = buildA;
    }
    public String getBuildB() {
        return buildB;
    }
    public void setBuildB(String buildB) {
        this.buildB = buildB;
    }
    public String getBuildC() {
        return buildC;
    }
    public void setBuildC(String buildC) {
        this.buildC = buildC;
    }
    public String getBuildD() {
        return buildD;
    }
    public void setBuildD(String buildD) {
        this.buildD = buildD;
    }
    @Override
    public String toString() {
        return buildA + "\n" +buildB + "\n" +buildC + "\n" +buildD + "\n" +"房子验收完成";
    }
}
代码示例:
package com.creation_Pattern.p04_builder;

/**
 * @author: 皮先生kx
 * @Description: 建造者抽象类
 * @date: created on 2021/10/17 19:20
 * @email: 2472177078@qq.com
 */
public abstract class Builder {

    //地基
    abstract void buildA();
    //钢筋工程
    abstract void buildB();
    //铺电线
    abstract void buildC();
    //粉刷
    abstract void buildD();
    //完工验收
    abstract Product getProduct();
}

代码示例:
package com.creation_Pattern.p04_builder;

/**
 * @author: 皮先生kx
 * @Description: 具体的建造者(建筑工人)
 * @date: created on 2021/10/17 19:20
 * @email: 2472177078@qq.com
 */
public class ConcreteBuilder extends Builder{

    private Product product;

    public ConcreteBuilder(){
        product = new Product();
    }

    @Override
    void buildA() {
        product.setBuildA("地基");
    }

    @Override
    void buildB() {
        product.setBuildB("钢筋工程");
    }

    @Override
    void buildC() {
        product.setBuildC("铺电线");
    }

    @Override
    void buildD() {
        product.setBuildD("粉刷");
    }

    @Override
    Product getProduct() {
        return product;
    }
}

代码示例:
package com.creation_Pattern.p04_builder;

/**
 * @author: 皮先生kx
 * @Description: 指挥者
 * @date: created on 2021/10/17 19:20
 * @email: 2472177078@qq.com
 */
public class Director {

    //指挥工人按顺序造房子
    public Product create(Builder builder){
        builder.buildA();
        builder.buildB();
        builder.buildC();
        builder.buildD();
        return builder.getProduct();

    }
}

代码示例:
package com.creation_Pattern.p04_builder;

/**
 * @author: 皮先生kx
 * @Description:
 * @date: created on 2021/10/17 19:20
 * @email: 2472177078@qq.com
 */
public class Test {

    public static void main(String[] args) {

        Director director = new Director();
        Product create = director.create(new ConcreteBuilder());
        System.out.println(create.toString());
    }
}

运行结果:
"C:\Program Files\Java\jdk1.8.0\bin\java.exe" 
地基
钢筋工程
铺电线
粉刷
房子验收完成

Process finished with exit code 0

4.2 通过静态的内部类方式实现零件无序装配化构造

1、角色

主要角色:抽象建造者、具体建造者、产品

比第一种少了指挥者,主要是因为第二种方式把指挥者交个用户来操作,使得产品创建根据简单灵活

2、步骤

1)创建建造者定义家具
2)创建实现家具
3)随意搭配家具
代码示例:
package com.creation_Pattern.p05_builder;

/**
 * @author: 皮先生kx
 * @Description: 家具
 * @date: created on 2021/10/17 19:18
 * @email: 2472177078@qq.com
 */
public class Product {

    //理解为ABCD四样家具
    private String buildA;
    private String buildB;
    private String buildC;
    private String buildD;

    public String getBuildA() {
        return buildA;
    }

    public void setBuildA(String buildA) {
        this.buildA = buildA;
    }

    public String getBuildB() {
        return buildB;
    }

    public void setBuildB(String buildB) {
        this.buildB = buildB;
    }

    public String getBuildC() {
        return buildC;
    }

    public void setBuildC(String buildC) {
        this.buildC = buildC;
    }

    public String getBuildD() {
        return buildD;
    }

    public void setBuildD(String buildD) {
        this.buildD = buildD;
    }

    @Override
    public String toString() {
        return buildA + "\n" +
                buildB + "\n" +
                buildC + "\n" +
                buildD + "\n" +"家具创建完成";

    }
}

代码示例:
package com.creation_Pattern.p05_builder;

/**
 * @author: 皮先生kx
 * @Description: 建造者
 * @date: created on 2021/10/17 19:20
 * @email: 2472177078@qq.com
 */
public abstract class Builder {

    //地基
    abstract Builder buildA(String msg);
    //钢筋工程
    abstract Builder buildB(String msg);
    //铺电线
    abstract Builder buildC(String msg);
    //粉刷
    abstract Builder buildD(String msg);
    //完工验收
    abstract Product build();
}

代码示例:
package com.creation_Pattern.p05_builder;

/**
 * @author: 皮先生kx
 * @Description: 具体的建造者(建筑工人)
 * @date: created on 2021/10/17 19:20
 * @email: 2472177078@qq.com
 */
public class ConcreteBuilder extends Builder {

    private Product product;

    public ConcreteBuilder(){
        product = new Product();
    }

    @Override
    Builder buildA(String msg) {
        product.setBuildA(msg);
        return this;
    }

    @Override
    Builder buildB(String msg) {
        product.setBuildB(msg);
        return this;
    }

    @Override
    Builder buildC(String msg) {
        product.setBuildC(msg);
        return this;
    }

    @Override
    Builder buildD(String msg) {
        product.setBuildD(msg);
        return this;
    }

    @Override
    Product build() {
        return product;
    }
}

代码示例:
package com.creation_Pattern.p05_builder;

/**
 * @author: 皮先生kx
 * @Description:
 * @date: created on 2021/10/17 19:20
 * @email: 2472177078@qq.com
 */
public class Test {

    public static void main(String[] args) {

        ConcreteBuilder concreteBuilder = new ConcreteBuilder();
        Product build = concreteBuilder.buildA("床").buildB("衣柜").buildC("沙发").buildD("电视柜").build();
        System.out.println(build.toString());
    }
}

运行结果:
"C:\Program Files\Java\jdk1.8.0\bin\java.exe" 
床
衣柜
沙发
电视柜
家具创建完成

Process finished with exit code 0

与工厂模式的区别:建造者模式根据关注与零件装配的顺序,一般用来创建较为复杂的对象

5、单例模式

定义:在任何给定时间只因运行一个类或某个类的一组预定义数量的实例

(1).饿汉模式

从名字是也可以很好理解,就是“比较勤快”,实例在初始化的时候就已经建好了,不管你有没有用到,都想创建好了再说

好处是没有线程安全的问题

坏处是浪费内存空间

代码示例:
package com.creation_Pattern.p06_singleton.t1;

/**
 * @author: 皮先生kx
 * @Description: 单例模式——饿汉模式(类装载的时候就是初始化对象)
 * @date: created on 2021/10/17 20:44
 * @email: 2472177078@qq.com
 */
public class Singleton {

    //属性要求私有静态
    private static  Singleton instance = new Singleton();

    //控制实例的数量,我们间构造器进行私有化,测试类Test中就不能使用new关键字创建对象了
    private Singleton(){

    }

    //上述两个都是私有的,外部都无法访问,当类装载的时候就是初始化对象,用的时候就可以通过下面的方法获得唯一一个实例
    public static Singleton getInstance(){
        return instance;
    }
}

代码示例:
package com.creation_Pattern.p06_singleton.t1;

/**
 * @author: 皮先生kx
 * @Description: 测试类
 * @date: created on 2021/10/17 20:44
 * @email: 2472177078@qq.com
 */
public class Test {
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            Singleton s = Singleton.getInstance();
            System.out.println(s);
        }
    }
}

运行结果:
"C:\Program Files\Java\jdk1.8.0\bin\java.exe" 

com.creation_Pattern.p06_singleton.t1.Singleton@4554617c
com.creation_Pattern.p06_singleton.t1.Singleton@4554617c
com.creation_Pattern.p06_singleton.t1.Singleton@4554617c
com.creation_Pattern.p06_singleton.t1.Singleton@4554617c
com.creation_Pattern.p06_singleton.t1.Singleton@4554617c

Process finished with exit code 0

(2).懒汉模式

实例在用到的时候才去创建,“比较懒” ,用的时候才去检查有没有实例,没有则创建

有线程安全和不安全的两种写法,区别就是synchronized关键字

代码示例:
package com.creation_Pattern.p06_singleton.t2;

/**
 * @author: 皮先生kx
 * @Description: 单例模式——懒汉模式(实例在用的时候再创建)
 * @date: created on 2021/10/17 20:44
 * @email: 2472177078@qq.com
 */
public class Singleton {

    //属性要求私有静态,没有初始化,此时的实例为null
    private static  Singleton instance;

    //控制实例的数量,我们间构造器进行私有化,测试类Test中就不能使用new关键字创建对象了
    private Singleton(){

    }

    //上述两个都是私有的,外部都无法访问
    //当多线程中两个线程同时第一次访问,同时为空,同时初始化容易出现两个实例对象,在此可以加入synchronized关键字,只能有一个线程访问,不好的地方就是影响效率
    public static synchronized Singleton getInstance(){
        if (instance==null){
            instance = new Singleton();
        }
        return instance;
    }
}

(3).双检锁

双检锁,又叫双重校验锁,在懒汉模式的基础上,通过synchronized关键字在内外都加了一层if条件判断,这样既保证了线程安全,又比直接上锁提高了执行效率,还节省了内存空间

代码示例:
package com.creation_Pattern.p06_singleton.t3;

/**
 * @author: 皮先生kx
 * @Description: 单例模式——双检锁
 * @date: created on 2021/10/17 20:44
 * @email: 2472177078@qq.com
 */
public class Singleton {

    //属性要求私有静态,没有初始化,此时的实例为null
    private volatile static  Singleton instance;

    //控制实例的数量,我们间构造器进行私有化,测试类Test中就不能使用new关键字创建对象了
    private Singleton(){

    }
    //解决效率低:方法加了synchronized,只能一个一个执行,并不能并发执行,效率低
    public static Singleton getInstance(){
        //当上面方法去掉synchronized,多个线程同时访问时,下面代码块不执行,直接return instance;
        if (instance==null){
            synchronized (Singleton.class){
                if (instance==null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
//可能造成指令重排,仍然存在不安全情况
//为了避免指令重排造成的不安全,需要加入volatiled,后面在CPU中执行中就不会指令重排

(4).静态内部类

静态内部类的方式效果类似双检锁,但是实现更加简单,当这种方式只适用于静态域的情况,双检锁方式可以在实例域需要延迟初始化时使用。

package com.creation_Pattern.p06_singleton.t4;

/**
 * @author: 皮先生kx
 * @Description: 单例模式——静态内部类
 * @date: created on 2021/10/17 20:44
 * @email: 2472177078@qq.com
 */
public class Singleton {
    
    //延迟装载,懒汉模式
    private Singleton(){

    }
    
    private static class SingletonHolder{
        private final static Singleton instance = new Singleton();
    }
    
    public static Singleton getInstance(){
        return SingletonHolder.instance;
    }
    
}

(5).枚举

枚举的方式更加简洁清晰,并且她还自动支持序列化机制,绝对防止多次实例化

代码示例:
package com.creation_Pattern.p06_singleton.t5;

/**
 * @author: 皮先生kx
 * @Description: 单例模式——枚举
 * @date: created on 2021/10/17 20:44
 * @email: 2472177078@qq.com
 */
public enum Singleton {

    //每一个枚举的类型都是一个静态的常量
    INSTANCE;

    public void doSomething(){
        System.out.println("doSomething .");
    }
}
代码示例:
package com.creation_Pattern.p06_singleton.t5;

/**
 * @author: 皮先生kx
 * @Description:
 * @date: created on 2021/10/17 21:15
 * @email: 2472177078@qq.com
 */
public class Test {
    public static void main(String[] args) {
        Singleton.INSTANCE.doSomething();
    }
}

6、原型模式

定义:用一个已经创建好 的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象

拷贝是直接在堆中进行,这其实也可以理解,new的时候,JVM要走一趟类加载流程,这个流程非常麻烦,在类加载流程中会调用构造函数,最后生成的对象会放在堆中国,而拷贝就是直接拷贝堆中的现成的二进制对象,然后重新分配一个内存块。

原型模式的克隆分为:浅克隆和深克隆

浅克隆:创建一个新对象,新对象的属性和原理对象完全相同,对于非基本数据类型,仍指向原有属性所指的对象的内存地址

深克隆:创建一个新对象,属性中应用的其他对象也会被克隆,不再指向原有对象地址

代码示例:
package com.creation_Pattern.p07_prototype;

//以该Student类作为克隆的原型
public class Student implements Cloneable{

    private String name;
    private int age;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    //重写父类方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
代码示例:
package com.creation_Pattern.p07_prototype;


public class Test {

    public static void main(String[] args) throws CloneNotSupportedException {

        Student s1 = new Student();
        s1.setName("张三");
        s1.setAge(20);

        Student s2 = (Student) s1.clone();
        System.out.println(s1);
        System.out.println(s1.getName()+","+s1.getAge());

        System.out.println(s2);
        System.out.println(s2.getName()+","+s2.getAge());
    }
}

运行结果:
"C:\Program Files\Java\jdk1.8.0\bin\java.exe"
com.creation_Pattern.p07_prototype.Student@4554617c
张三,20
com.creation_Pattern.p07_prototype.Student@74a14482
张三,20

Process finished with exit code 0
//可以看到属性相同,但是地址是不一样的

原型模式的优点:

  • Java自带的原型模式基于内存二进制流的复制,在性能上比直接new一个对象更加优良
  • 可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一个状态),可辅助实现撤销操作

原型模式的缺点:

  • 需要为每一个类都配置一个clone方法
  • clone方法位于类的内存,当对已有类进行改造的时候,需要修改代码,违背了开闭原则
  • 当实现深克隆的时候,需要编写较为复杂的代码,而且当对象之间存在多重嵌套应用是,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来比较麻烦。因此深克隆、浅克隆需要运行得当。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值