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

设计模式05-创建型模式(建造者/原型/单例模式/Java)

3.4 建造者模式

3.4.1 建造者模式的定义

动机:方便创建复杂对象(一个对象中包含多个成员变量)

定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。

3.4.2 建造者模式的分析与实现

image-20241019182144932

  • 产品类:复杂对象,里面包含多个成员变量
public class Product {
    private String part1;
    private String part2;

    public String getPart1() {
        return part1;
    }

    public void setPart1(String part1) {
        this.part1 = part1;
    }

    public String getPart2() {
        return part2;
    }

    public void setPart2(String part2) {
        this.part2 = part2;
    }
}
  • 建造者类:内含两类方法,一个是建造构件,一个是返回建造对象
public abstract class Builder {
    protected Product product = new Product();
    public abstract void buildPartA();
    public abstract void buildPartB();
    public abstract Product getResult();
}

public class ConcreteBuilderA extends Builder{
    @Override
    public void buildPartA() {
        product.setPart1("部件A");
    }

    @Override
    public void buildPartB() {
        product.setPart2("部件B");
    }

    @Override
    public Product getResult() {
        return product;
    }
}
  • 指挥者类:负责构建的建造次序,并返回产品类
public class Director {
    private Builder builder;
    public Director(Builder builder) {
        this.builder = builder;
    }
    public Product construct() {
        builder.buildPartA();
        builder.buildPartB();
        return builder.getResult();
    }
}
  • 客户端类:只需要指定具体的建造者即可,无需关心产品的具体组装过程
public class Main {
    public static void main(String[] args) {
        Director director = new Director(new ConcreteBuilderA());
        Product construct = director.construct();
        System.out.println(construct.getPart1());
        System.out.println(construct.getPart2());
    }
}
3.4.3 建造者模式案例

建造者模式可以用于描述KFC如何创建套餐:套餐是一个复杂对象,它一般包含主食(如汉堡、鸡肉卷等)和饮料(如果汁、可乐等)等组成部分,不同的套餐有不同的组成部分,而KFC的服务员可以根据顾客的要求,一步一步装配这些组成部分,构造一份完整的套餐,然后返回给顾客。

image-20241019184000936
  • 产品类
public class Meal {
    private String food;
    private String drink;

    public String getFood() {
        return food;
    }

    public void setFood(String food) {
        this.food = food;
    }

    public String getDrink() {
        return drink;
    }

    public void setDrink(String drink) {
        this.drink = drink;
    }
}
  • 建造者类
public interface MealBuilder {

    public void buildFood();
    public void buildDrink();
    public Meal getMeal();
}

public class MealBuilder1 implements MealBuilder {
    private Meal meal = new Meal();
    @Override
    public void buildFood() {
        meal.setFood("汉堡");
    }

    @Override
    public void buildDrink() {
        meal.setDrink("可乐");
    }

    @Override
    public Meal getMeal() {
        return meal;
    }
}

public class MealBuilder2 implements MealBuilder {
    private Meal meal = new Meal();

    @Override
    public void buildFood() {
        meal.setFood("鸡肉卷");
    }

    @Override
    public void buildDrink() {
        meal.setDrink("果汁");
    }

    @Override
    public Meal getMeal() {
        return meal;
    }
}
  • 指挥者类
public class KFCWaiter {
    private MealBuilder mealBuilder;

    public void setMealBuilder(MealBuilder mealBuilder) {
        this.mealBuilder = mealBuilder;
    }

    public Meal constructMeal() {
        mealBuilder.buildFood();
        mealBuilder.buildDrink();
        return mealBuilder.getMeal();
    }
}
  • 客户端
public class Main {
    public static void main(String[] args) {
        MealBuilder2 mealBuilder= (MealBuilder2) XMLUtilMeal.getMealMethod();
        KFCWaiter kfcWaiter = new KFCWaiter();
        kfcWaiter.setMealBuilder(mealBuilder);
        Meal meal = kfcWaiter.constructMeal();
        System.out.println("套餐2");
        System.out.println("主食为:" + meal.getFood());
        System.out.println("饮品为:" + meal.getDrink());
    }
}
3.4.4 建造者模式的优缺点
优点缺点
1.实现了产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象,精细地控制产品的创建过程1.同一个抽象构造者只适用构建一类型相同的产品,若产品间差别太大不适用
2.由于引入抽象建造者,因此很容易引入新的创建者,符合开闭原则2.产品内部构造复杂,会引起系统庞大,难以构建
3.4.5 建造者模式的适用场景
  • 需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员变量,且需要生成的产品对象的属性相互依赖,需要指定其生成顺序

  • 对象的创建过程独立于创建该对象的类。在建造者模式中通过引入了指挥者类,将创建过程封装在指挥者类中,而不在建造者类和客户类中

3.5 原型模式

3.5.1 原型模式的定义

动机:有些对象的创建过程较为复杂,而且需要频繁创建。通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象。

定义:用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。原型模式允许通过一个原型对象创建一个或多个同类型的其他对象,而无须知道任何创建的细节

3.5.2 原型模式的分析与实现

将一个原型对象传给要发动创建的对象(即客户端对象),这个要发动创建的对象通过请求原型对象复制自己来实现创建过程。创建新对象(也称为克隆对象)的工厂就是原型类自身,工厂方法由负责复制原型对象的克隆方法来实现。通过克隆方法所创建的对象是全新的对象,它们在内存中拥有新的地址,每一个克隆对象都是独立

image-20241019193531286
  • 浅克隆:只复制它本身和其中包含的值类型的成员变量,而引用类型的成员变量并没有复制
image-20241019193926199

在Java中可以直接使用Object提供的clone()方法来实现对象的克隆(浅克隆)

能够实现克隆的Java类必须实现一个标识接口Cloneable,表示这个Java类支持复制

如果一个类没有实现这个接口但是调用了clone()方法,Java编译器将抛出一个CloneNotSupportedException异常

public class ShallowPrototype implements Cloneable{
    public String name;
    @Override
    public ShallowPrototype clone() {
        try {
            return (ShallowPrototype) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }
}
  • 深克隆:除了对象本身被复制外,对象所包含的所有成员变量也将被复制
image-20241019193952167

如果需要实现深克隆,可以通过序列化等方式来实现。在Java语言中,序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存中。**通过序列化实现的拷贝不仅可以复制对象本身,而且可以复制其引用的成员对象,因此通过序列化将对象写到一个流中,再从流里将其读出来,从而实现深克隆。**需要注意的是,能够实现序列化的对象其类必须实现Serializable接口,否则无法实现序列化操作。

public class DeepPrototype implements Serializable {
    public String name = "sda";
    public DeepPrototype clone() {
        try {
            //将对象写到输入流中
            ByteArrayOutputStream bao = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bao);
            oos.writeObject(this);
            //将对象从输入流中取出
            ByteArrayInputStream bais = new ByteArrayInputStream(bao.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            return (DeepPrototype) ois.readObject();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
  • 克隆的结果
1)对任何的对象x,都有x.clone()!=x,即克隆对象与原对象不是同一个对象。
(2)对任何的对象x,都有x.clone().getClass()==x.getClass(),即克隆对象与原对象的类型一样。
3.5.3 原型模式的案例

由于邮件对象包含的内容较多(如发送者、接收者、标题、内容、日期、附件等),某系统中现需要提供一个邮件复制功能,对于已经创建好的邮件对象,可以通过复制的方式创建一个新的邮件对象,如果需要改变某部分内容,无须修改原始的邮件对象,只需要修改复制后得到的邮件对象即可。使用原型模式设计该系统。

浅克隆:

image-20241019201056294
public class EmailPrototype implements Cloneable {
    private File file = null;

    public EmailPrototype() {
        this.file = new File();
    }

    public void display() {
        System.out.println("展示所下载的文件");
    }

    public File getFile() {
        return this.file;
    }

    public EmailPrototype close() {
        try {
            return (EmailPrototype) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }
}

深克隆:

image-20241019201113430

public class EmailPrototype implements Serializable {
    private File file = null;

    public EmailPrototype() {
        this.file = new File();
    }

    public void display() {
        System.out.println("展示所下载的文件");
    }

    public File getFile() {
        return this.file;
    }

    public EmailPrototype deepClone() {
        try {
            //将对象转化写入到字节流中
            ByteArrayOutputStream bao = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bao);
            oos.writeObject(this);

            //将对象从字节流中取出
            ByteArrayInputStream bis = new ByteArrayInputStream(bao.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            return (EmailPrototype) ois.readObject();
        } catch (Exception e) {
           throw new RuntimeException(e);
        }
    }
}
3.5.4 原型模式的优缺点
优点缺点
1.简化对象的创建过程,通过复制一个已有实例可以提高新实例的创建效率1.需要为每一个类配备一个克隆方法,当对已有的类进行改造时,需要修改源代码,违背了开闭原则。
2.可以使用深克隆的方式保存对象的状态,以便在需要的时候使用,可辅助实现撤销操作
3.对于相似的对象,可以修改其属性使得各对象不同
3.5.5 原型模式的适用场景
  • 创建新对象成本较大,新对象可以通过复制已有对象来获得,如果是相似对象,则可以对其成员变量稍作修改

  • 系统要保存对象的状态,而对象的状态变化很小

  • 需要避免使用分层次的工厂类来创建分层次的对象

3.5.6 原型模式的扩展

通过原型管理器,管理所有可以复制的对象

将多个原型对象存储在一个集合中供客户端使用,它是一个专门负责克隆对象的工厂,其中定义了一个集合用于存储原型对象,如果需要某个原型对象的一个克隆,可以通过复制集合中对应的原型对象来获得。

image-20241019201624755
public class PrototypeManager {
    private Hashtable prototypeTable=new Hashtable();  
                                                                    //Hashtable存储原型对象
    public PrototypeManager() {
        prototypeTable.put("A", new ConcretePrototypeA());
        prototypeTable.put("B", new ConcretePrototypeB());
    }   
    public void add(String key, Prototype prototype) {
        prototypeTable.put(key,prototype);
    } 
    public Prototype get(String key) {
        Prototype clone = null;
        clone = ((Prototype)prototypeTable.get(key)).clone(); //克隆方法创建新对象
        return clone;
    }    
}

3.6 单例模式

3.6.1 单例模式的定义

动机:如何确保一个类只有一个实例并且这个实例易于被访问

定义:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类

3.6.2 单例模式的分析与实现

image-20241019202136689

  • 唯一实例:为了防止客户端程序使用构造函数来创建多个对象,可以将构造函数声明为私有的,这样客户端程序就不可以使用它来创建任何对象。

  • 提供访问:通过创建一个全局变量和一个getInstance()静态方法,用来让外部客户端访问

  • 分类:

**懒汉式:**该模式只在你需要对象时才会生成单例对象(比如调用getInstance方法)

public class Singleton1 {
    private static Singleton1 singleton = null;
    private Singleton1() {}
    public static Singleton1 get() {
        if (singleton == null) {
            singleton = new Singleton1();
        } else {
            System.out.println("此对象已经被创建过了");
        }
        return singleton;
    }
}

**饿汉式:**该模式在类被加载时就会实例化一个对象。

public class Singleton2 {
    private static Singleton2 singleton = new Singleton2();
    private Singleton2() {}
    public static Singleton2 get() {
        return singleton;
    }
}
懒汉式饿汉式
实例化外部调用时类加载时
线程安全不安全安全
执行效率较低较高
内存不浪费浪费
3.6.3 单例模式的案例

在操作系统中,打印池(Print Spooler)是一个用于管理打印任务的应用程序,通过打印池用户可以删除、中止或者改变打印任务的优先级,在一个系统中只允许运行一个打印池对象,如果重复创建打印池则抛出异常。现使用单例模式来模拟实现打印池的设计。

image-20241019203315755
public class Printer {
    private static Printer printer = null;

    private Printer() {}

    public static Printer getInstance() {
        if (printer == null) {
            System.out.println("获取打印机资源");
            printer = new Printer();
        } else {
            try {
                System.out.println("打印机资源已被占用");
                int x = 1/0;
            } catch (Exception e) {
                throw new RuntimeException("等待打印机释放资源");
            }
        }
        return printer;
    }

    public void manage() {
        System.out.println("打印机正常工作");
    }

    public void revoke() {
        printer = null;
    }
}
3.6.4 单例模式的优缺点
优点缺点
1.提供唯一实例的受控访问1.扩展困难
2.节约资源2.单例类的职责过重,违背“单一职责原则”
3.允许可变数目的实例3.由于自动垃圾回收机制,可能会导致共享的单例对象的状态丢失
3.6.5 单例模式的适用场景
  • 系统只需要一个实例对象,或者因为资源消耗太大而只允许创建一个对象

  • 客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例


http://www.kler.cn/news/361898.html

相关文章:

  • el-table在某些条件下禁止选中
  • 写一个 qq自动回话的程序
  • GJB438C-2021《软件需求规格说明》的一处修订
  • 3DMAX碎片生成器插件FragmentGenerator使用方法
  • 【Python】爬虫
  • 出血性脑卒中临床智能诊疗建模
  • 使用js和canvas实现绘制一只丑萌的小猫,一步步绘制
  • 电感的学习
  • Tomcat怎么调整参数以优化性能
  • 【MySQL备份】Percona XtraBackup
  • 中医大模型开源!数据集开源!自己训练一个中医大模型吧!
  • 简单介绍冯诺依曼体系
  • 深入理解 JavaScript 中的剩余参数和扩展运算符
  • 对比学习)
  • C++ 标准库:功能与应用解析
  • 考研408考试科目之计算机数据结构在科技应用——未来之窗学习通
  • 安卓設備上怎麼設置HTTP代理?
  • IIS不能使用Delete方法
  • Spring事务底层源码解析(二)
  • 大数据分析案例-基于随机森林模型的机器学习工程师岗位薪资预测
  • SQLI LABS | Less-3 GET-Error based-Single quotes with twist-String
  • 11种经典时间序列预测方法:理论、Python实现与应用
  • Linux云计算 |【第五阶段】ARCHITECTURE-DAY4
  • LabVIEW水质监测系统
  • leetcode 3191. 使二进制数组全部等于 1 的最少操作次数 I 中等
  • 计算机的错误计算(一百三十一)