设计模式 - 创建型模式考点篇:工厂模式、建造者模式

news2024/11/23 12:27:34

目录

一、创建型模式

一句话概括

1.1、工厂模式

1.1.1、简单工厂模式(非 23 种经典设计模式)

概述

案例

1.1.2、静态工厂(扩展)

1.1.3、工厂方法模式

概念

案例

1.2、建造者模式

1.2.1、概念

1.2.2、案例

1.2.3、建造者模式扩展:链式编程底层

1.3、工厂方法模式 VS 建造者模式


一、创建型模式


一句话概括

创建型模式:教你如果创建对象.

1.1、工厂模式

1.1.1、简单工厂模式(非 23 种经典设计模式)

概述

简单工厂不是一种设计模式,反而比较像是一种编程习惯.

简单工厂包含一下角色:

  • 抽象产品 :定义了产品的规范,描述了产品的主要特性和功能。
  • 具体产品 :实现或者继承抽象产品的子类
  • 具体工厂 :提供了创建产品的方法,调用者通过该方法来获取产品

案例

需求:设计一个咖啡店点餐系统.

思路:设计一个咖啡类(Coffee),并定义其两个子类(美式咖啡【AmericanCoffee】和拿铁咖啡 【LatteCoffee】);再设计一个咖啡店类(CoffeeStore),咖啡店具有点咖啡的功能。

以下代码为空架.

/**
 * 咖啡
 */
public abstract class Coffee {

    public abstract String getName();

    public void addSugar() {
        System.out.println("加糖");
    }

    public void addWeight() {
        System.out.println("加量");
    }

}
/**
 * 美式咖啡
 */
public class AmericanCoffee extends Coffee{

    @Override
    public String getName() {
        return "美式咖啡";
    }

}
/**
 * 拿铁咖啡
 */
public class LatteCoffee extends Coffee{

    @Override
    public String getName() {
        return "拿铁咖啡";
    }

}
/**
 * 咖啡店
 */
public class CoffeeStore {

    public Coffee orderCoffee(String type) {
        //1.根据订单类型选取对应的咖啡
        Coffee coffee = null;
        if (type == null || type.equals("")) {
            throw new RuntimeException("type 非法");
        } else if (type.equals("ac")) {
            coffee = new AmericanCoffee();
        } else if(type.equals("lc")) {
            coffee = new LatteCoffee();
        } else {
            throw new RuntimeException("type 非法");
        }
        //2.加糖加量
        coffee.addSugar();
        coffee.addWeight();
        return coffee;
    }

}
/**
 * 测试客户端
 */
public class ClientCoffee {

    public static void main(String[] args) {
        CoffeeStore store = new CoffeeStore();
        Coffee coffee = store.orderCoffee("ac");
        System.out.println(coffee.getName());
    }

}

可以产出上述代码中 Coffee 类为抽象产品,AmericanCoffee 和 LatteCoffee 为具体产品,但是没有抽象工厂.  以下使用 简单工厂模式 对上述代码进行改造.

public abstract class Coffee {

    public abstract String getName();

    public void addSugar() {
        System.out.println("加糖");
    }

    public void addWeight() {
        System.out.println("加量");
    }

}
public class AmericanCoffee extends Coffee {

    @Override
    public String getName() {
        return "美式咖啡";
    }

}
public class LatteCoffee extends Coffee {

    @Override
    public String getName() {
        return "拿铁咖啡";
    }

}
/**
 * 简单工厂模式
 */
public class SimpleCoffeeFactory {

    public Coffee createCoffee(String type) {
        //1.根据订单类型选取对应的咖啡
        Coffee coffee = null;
        if (type == null || type.equals("")) {
            throw new RuntimeException("type 非法");
        } else if (type.equals("ac")) {
            coffee = new AmericanCoffee();
        } else if(type.equals("lc")) {
            coffee = new LatteCoffee();
        } else {
            throw new RuntimeException("type 非法");
        }
        //2.加糖加量
        coffee.addSugar();
        coffee.addWeight();
        return coffee;
    }

}
public class CoffeeStore {

    public Coffee orderCoffee(String type) {
        //1.让工厂来创建对象
        SimpleCoffeeFactory factory = new SimpleCoffeeFactory();
        Coffee coffee = factory.createCoffee(type);
        //2.加料
        coffee.addWeight();
        coffee.addSugar();
        return coffee;
    }

}
public class ClientCoffee {

    public static void main(String[] args) {
        CoffeeStore store = new CoffeeStore();
        Coffee coffee = store.orderCoffee("ac");
        System.out.println(coffee.getName());
    }

}

上述代码中有了 SimpleCoffeeFactory ,使得 Coffee 的具体实现类和 CoffeeStore 解耦合,但是又产生了 CoffeeStore 和 SimpleCoffeeFactory 对象的耦合. 后期我们如果要加新的咖啡品种,势必需要修改 SimpleCoffeeFactory 的代码,违反了开闭原则.

优点:

解耦合:将对象的创建和业务逻辑层分开,避免将来修改客户端代码.  入股偶要实现新产品,直接修改工厂类,不需要再原客户端代码,更容易扩展.

缺点:

违背“开闭原则”:增加新产品时还需要修改工厂类代码.

1.1.2、静态工厂(扩展)

在实际的开发中也有一部分人将工厂类中的创建对象的功能定义为静态的,这个就是静态工厂模式,避免对象的重复创建.  他也不是属于 23 中设计模式中的.

/**
 * 简单工厂模式
 */
public class StaticCoffeeFactory {

    public static Coffee createCoffee(String type) {
        //1.根据订单类型选取对应的咖啡
        Coffee coffee = null;
        if (type == null || type.equals("")) {
            throw new RuntimeException("type 非法");
        } else if (type.equals("ac")) {
            coffee = new AmericanCoffee();
        } else if(type.equals("lc")) {
            coffee = new LatteCoffee();
        } else {
            throw new RuntimeException("type 非法");
        }
        //2.加糖加量
        coffee.addSugar();
        coffee.addWeight();
        return coffee;
    }

}

1.1.3、工厂方法模式

概念

定义一个用于创建对象的接口,让子类决定实例化哪个产品类对象。工厂方法使一个产品类的实例化延迟到其工厂的子类。

工厂方法模式包含以下角色:

  • 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂 方法来创建产品。
  • 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
  • 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
  • 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同 具体工厂之间一一对应。
案例

需求:设计一个咖啡店点餐系统,咖啡种类有美式咖啡和拿铁咖啡.

/**
 * 抽象产品: 咖啡类
 */
public abstract class Coffee {

    public abstract String getName();

    public void addSugar() {
        System.out.println("加糖");
    }

    public void addWeight() {
        System.out.println("加量");
    }

}
/**
 * 具体产品:美式咖啡类
 */
public class AmericanCoffee extends Coffee {

    @Override
    public String getName() {
        return "美式咖啡";
    }

}
/**
 * 具体产品:拿铁咖啡类
 */
public class LatteCoffee extends Coffee {

    @Override
    public String getName() {
        return "拿铁咖啡";
    }

}
/**
 * 抽象工厂: 咖啡工厂
 */
public interface CoffeeFactory {

    Coffee createCoffee();

}
/**
 * 具体工厂:美式咖啡工厂
 */
public class AmericanCoffeeFactory implements CoffeeFactory{

    @Override
    public Coffee createCoffee() {
        return new AmericanCoffee();
    }

}
/**
 * 具体工厂:拿铁咖啡工厂
 */
public class LatteCoffeeFactory implements CoffeeFactory{

    @Override
    public Coffee createCoffee() {
        return new LatteCoffee();
    }

}
/**
 * 咖啡商店
 */
public class CoffeeStore {

    private CoffeeFactory factory;

    public void setFactory(CoffeeFactory factory) {
        this.factory = factory;
    }

    public Coffee orderCoffee() {
        //1.创建咖啡对象
        Coffee coffee = factory.createCoffee();
        //2.加料
        coffee.addWeight();
        coffee.addSugar();
        return coffee;
    }

}
public class ClientCoffee {

    public static void main(String[] args) {
        CoffeeStore store = new CoffeeStore();
//        AmericanCoffeeFactory factory = new AmericanCoffeeFactory();
        LatteCoffeeFactory factory = new LatteCoffeeFactory();
        store.setFactory(factory);
        Coffee coffee = store.orderCoffee();
        System.out.println(coffee.getName());
    }

}

优点:

使用便利:用户只需要知道具体的工厂名称就可以拿到产品,无须知道产品的具体创建过程.

满足“开闭原则”:当需要添加新的产品时,无需对原工厂进行修改,只需要添加具体的产品类和工厂即可.

缺点:

“类爆炸”:每增加一个产品就需要增加一个具体产品类和一个对应的具体工厂类,增加系统复杂度.

使用场景:

  • 重复代码 : 创建对象需要使用大量重复的代码 ;
  • 不关心创建过程 : 客户端 不依赖 产品类 , 不关心 实例 如何被创建 , 实现等细节 ;
  • 创建对象 : 一个类通过其子类来指定创建哪个对象 ;

1.2、建造者模式

1.2.1、概念

将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。例如电脑的主机由 cpu、内存、主板、显卡 构成,用户只需要指定主机的类型就可以得到相应的主机,无需知道内部的具体构造细节.

建造者模式包含如下角色:

  • 抽象建造者类(Builder):这个接口规定要实现复杂对象的那些部分的创建,并不涉及具体的部件对象的创建。
  • 具体建造者类(ConcreteBuilder):实现 Builder 接口,完成复杂产品的各个部件的具体 创建方法。在构造过程完成后,提供产品的实例。
  • 产品类(Product):要创建的复杂对象。
  • 指挥者类(Director):调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产 品的信息,只负责保证对象各部分完整创建或按某种顺序创建。

1.2.2、案例

生产自行车是一个复杂的过程,它包含了车架,车座等组件的生产。而车架又有碳纤维,铝合金等材质 的,车座有橡胶,真皮等材质。对于自行车的生产就可以使用建造者模式。

/**
 * 产品类:自行车类
 */
public class Bike {

    //车架
    private String frame;
    //车座
    private String seat;

    public String getFrame() {
        return frame;
    }

    public void setFrame(String frame) {
        this.frame = frame;
    }

    public String getSeat() {
        return seat;
    }

    public void setSeat(String seat) {
        this.seat = seat;
    }

}
/**
 * 抽象建造者类
 */
public abstract class Builder {

    //这里只是一个空架子,还需要具体实现
    protected Bike bike = new Bike();

    //自行车的组成部分
    public abstract void buildFrame();
    public abstract void buildSeat();

    //构建自行车
    public abstract Bike createBike();

}
/**
 * 具体建造者类:捷安特建造者类
 */
public class GiantBuilder extends Builder{

    @Override
    public void buildFrame() {
        this.bike.setFrame("碳纤维车架");
    }

    @Override
    public void buildSeat() {
        this.bike.setSeat("橡胶车座");
    }

    @Override
    public Bike createBike() {
        return this.bike;
    }

}
/**
 * 具体建造者类:摩拜建造者类
 */
public class MobikeBuilder extends Builder {

    @Override
    public void buildFrame() {
        this.bike.setFrame("铝合金车架");
    }

    @Override
    public void buildSeat() {
        this.bike.setSeat("真皮车座");
    }

    @Override
    public Bike createBike() {
        return this.bike;
    }

}
/**
 * 指挥者类
 */
public class Director {

    private Builder builder;

    public Director(Builder builder) {
        this.builder = builder;
    }

    /**
     * 指导构造
     * @return
     */
    public Bike construct() {
        //开始构建
        builder.buildFrame();
        builder.buildSeat();
        return builder.createBike();
    }

}
/**
 * 测试类
 */
public class Client {

    public static void main(String[] args) {
        //建造捷安特自行车
        showBike(new GiantBuilder());
        //建造摩拜自行车
        showBike(new MobikeBuilder());
    }

    private static void showBike(Builder builder) {
        Director director = new Director(builder);
        Bike bike = director.construct();
        System.out.println(bike.getFrame());
        System.out.println(bike.getSeat());
    }

}

优点:

解耦合:客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得 相同的创建过程可以创建不同的产品对象。

易扩展,符合开闭原则:如果有新的需求,通过实现一个新的建造者就可以完成,基本上不用修改其他代码.

缺点:

如果产品之间的差异性比较大,则不适合使用,因此使用范围受到一定的限制.

使用场景:

创建对象的过程比较负责,由多个部件构成,但构建的顺序是稳定的.

产品的构建过程和最终的表示是独立的.

1.2.3、建造者模式扩展:链式编程底层

建造者模式除了上述用途以外,在开发中还有一种常用的使用方式,就是当一个类构造器需要传入多个参数,此时创建这个类的实例,代码的可读性就会非常差,而且很容易引入新的错误. 此时就可以使用 链式编程底层 的方式对 建造者模式进行重构.

 重构后代码如下:

public class Phone {

    private String cpu;
    private String screen;
    private String memory;
    private String mainBoard;

    //私有构造,只对内提供
    private Phone(Builder builder) {
        this.cpu = builder.cpu;
        this.screen = builder.screen;
        this.memory = builder.memory;
        this.mainBoard = builder.mainBoard;
    }

    @Override
    public String toString() {
        return "Phone{" +
                "cpu='" + cpu + '\'' +
                ", screen='" + screen + '\'' +
                ", memory='" + memory + '\'' +
                ", mainBoard='" + mainBoard + '\'' +
                '}';
    }

    //链式编程构建
    public static class Builder {
        private String cpu;
        private String screen;
        private String memory;
        private String mainBoard;

        public Builder cpu(String cpu) {
            this.cpu = cpu;
            return this;
        }

        public Builder screen(String screen) {
            this.screen = screen;
            return this;
        }

        public Builder memory(String memory) {
            this.memory = memory;
            return this;
        }

        public Builder mainBoard(String mainBoard) {
            this.mainBoard = mainBoard;
            return this;
        }

        public Phone build() {
            return new Phone(this);
        }
    }

}
public class Client {

    public static void main(String[] args) {
        Phone phone = new Phone.Builder()
                .cpu("intel")
                .screen("三星屏幕")
                .mainBoard("华硕")
                .memory("金士顿")
                .build();
        System.out.println(phone);
    }

}

重构后使用起来更加方便,提高开发效率.  但是从软件设计上,对程序员的要求比较高.

1.3、工厂方法模式 VS 建造者模式

工厂方法模式注重是整体对象的创建方式;而建造者模式注重的是部件一步一步的创建出一个复杂对象的过程 .

比如我要制造一个超人,如果使用工厂方法模式,直接产生出来就是一个力大无穷、能飞,内裤外穿的超人;而如果使用建造者模式,则需要组装 手、头、脚、躯干、裤头... 一个超人就诞生了.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1071759.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Deep learning of free boundary and Stefan problems论文阅读复现

Deep learning of free boundary and Stefan problems论文阅读复现 摘要1. 一维一相Stefan问题1.1 Direct Stefan problem1.2 Inverse Type I1.3 Inverse Type II 2. 一维二相Stefan问题2.1 Direct Stefan problem2.2 Inverse Type I2.3 Inverse Type II 3. 二维一相Stefan问题…

《视觉 SLAM 十四讲》第 7 讲 视觉里程计1 【如何根据图像 估计 相机运动】【特征点法】

github源码链接V2 文章目录 第 7 讲 视觉里程计17.1 特征点法7.1.1 特征点7.1.2 ORB 特征FAST 关键点 ⟹ \Longrightarrow ⟹ Oriented FASTBRIEF 描述子 7.1.3 特征匹配 7.2 实践 【Code】本讲 CMakeLists.txt 7.2.1 使用 OpenCV 进行 ORB 的特征匹配 【Code】7.2.2 手写 O…

windows 2003、2008远程直接关闭远程后设置自动注销会话

1、2003系统: 按开始—运行—输入“tscc.msc”,打开“终端服务配置”。 单击左边窗口的“连接”项,右边窗口中右击“RDP-TCP”,选择“属性”。 单击“会话”项,勾选“替代用户设置”,在“结束已断开的会话”…

C语言中文网 - Shell脚本 - 2

第1章 Shell基础(开胃菜) 2. Shell是运维人员必须掌握的技能 Linux 运维人员就是负责 Linux 服务器的运行和维护。随着互联网的爆发,Linux 运维在最近几年也迎来了春天,出现了大量的职位需求,催生了一批 Linux 运维培…

远程实时监控管理:5G物联网技术助力配电站管理

配电站远程监控管理系统是基于物联网和大数据处理等技术的一种创新解决方案。该系统通过实时监测和巡检配电场所设备的状态、环境情况、安防情况以及火灾消防等信息,实现对配电站的在线实时监控与现场设备数据采集。 配电站远程监控管理系统通过回传数据进行数据系…

Logback日志框架使用详解以及如何Springboot快速集成

Logback简介 日志系统是用于记录程序的运行过程中产生的运行信息、异常信息等&#xff0c;一般有8个级别&#xff0c;从低到高为All < Trace < Debug < Info < Warn < Error < Fatal < OFF off 最高等级&#xff0c;用于关闭所有日志记录fatal 指出每个…

LSM-Tree笔记

假设Level 0为内存中的Buffer&#xff0c;容量为 B B B&#xff0c;层与层之间的条目数量差 T T T 倍 Tiered Level 1共有 T T T 个runs&#xff0c;每个run的容量均为 B B BLevel 2共有 T T T 个runs&#xff0c;每个run的容量均为 T ⋅ B T\cdot B T⋅BLevel n共有 …

周记学习总结

10.3 今天加载出来了一下歌词&#xff0c;并且画了一下旁边的简单动画&#xff0c;然后画了一下下面的评论&#xff0c;今天主要是看了好多歌词滚动并且让它居中的&#xff0c;一直用的是scrollIntoView这个函数&#xff0c;但是这个函数似乎一直没有用&#xff0c;今天了解了…

多自由度工业机械臂机电系统

经过数百万年的进化创造了最通用和完善的工具——人类手臂。我们现代世界中的一切都得益于这个工具。即使到今天&#xff0c;工业界也没有找到比机器人手臂更多功能的工具来在三维空间中操纵物体&#xff0c;机器人手臂本质上是人类手臂的机电复制品。机器人手臂的多功能性确实…

企业关于低代码的需求——PDM 元数据电子审批流

企业关于低代码的需求 PDM 元数据电子审批流 审批流业务场景是现代企业运营中不可或缺的一环。业务流程从某个特定点开始,然后经过一系列的审批节点,完成流程的审批。这些节点通常由不同级别的人员担任,例如主管、经理、财务、法务和总经理等,每个人都扮演着特定的角色和…

阿里云数据库MongoDB恢复到本地

共两种方式&#xff0c;建议使用第二种的逻辑恢复&#xff0c;比较方便快捷 一、下载物理备份文件 下载的格式是xb的&#xff0c;主要跟实例创建时间有关&#xff0c;2019年03月26日之前创建的实例&#xff0c;物理备份文件格式为tar&#xff0c;后面全部都是xb的格式了&#…

DownloadingImages 下载缓存图片,显示图片文字列表

1. 用到的技术点: 1) Codable : 可编/解码 JSON 数据 2) background threads : 后台线程 3) weak self : 弱引用 4) Combine : 取消器/组合操作 5) Publishers and Subscribers : 发布者与订阅者 6) FileManager : 文件管理器 7) NSCache : 缓存 2. 网址: 2.1 测试接口网址: …

docker入门加实战—docker安装并配置阿里云加速

docker入门加实战—docker安装并配置阿里云加速 为什么要学习docker 在开发和部署项目的过程中&#xff0c;经常会遇到如下问题&#xff1a; 软件安装包名字复杂&#xff0c;不知道去哪里找安装软件和部署项目步骤复杂&#xff0c;容易出错 这就是我们今天要学习Docker技术…

7个在Github上的flutter开源程序

阅读大量代码是提高开发技能的最佳方法之一。该开源项目是了解最佳实践、编码风格和许多其他主题的最佳场所。 软件开发最受欢迎的领域之一是跨平台移动应用程序开发。Flutter 是您可以使用的最流行的跨平台移动应用程序开发工具之一。今天&#xff0c;我们将了解 7 个开源 Flu…

sqlalchemy 连接池

报错 sqlalchemy.exc.TimeoutError: QueuePool limit of size 100 overflow 10 reached, connection timed out, timeout 30 (Background on this error at: http://sqlalche.me/e/3o7r) 查看数据库未活动超时时间 show variables like "interactive_timeout";一般…

R可视乎|灯芯柱状图代码解读

简介 这篇推文代码来源于&#xff1a;TidyTuesday&#xff0c;主要想学习如何绘制灯芯柱状图&#xff08;名字小编瞎取的&#xff09;&#xff0c;最终结果如下&#xff1a; 注释&#xff1a;与普通柱状图相比&#xff0c;灯芯柱状图不仅可以展示随时间变化的总体趋势&#xf…

AQS内部的体系架构

AQS本质上是一个双向队列&#xff0c;加一个状态位state。内部靠Node节点形成队列。 AQS由state和CLH变体的虚拟双端队列组成。 AQS的内部类Node类 属性说明&#xff1a; 内部结构&#xff1a;

继续改进 另外一种方法 还是可以用CourseExend类

优势&#xff1a;可以映射许多类上 很强大 用在名称不统一上 原理 1.先把查询结果包装 取新别名&#xff08;&#xff08; 就是结果集的映射&#xff08;&#xff09; 2.把中结果集映射相应的pojo类上 第一步基于类创建 第二步 注意字属性的写法 teacher 是属性 属性的属性 …

手机待办事项app哪个好?

手机是日常很多人随身携带的设备&#xff0c;手机除了拥有通讯功能外&#xff0c;还能帮助大家高效管理日常工作&#xff0c;借助手机上的待办事项提醒APP可以快速地帮助大家规划日常事务&#xff0c;提高工作的效率。 过去&#xff0c;我也曾经在寻找一款能够将工作任务清晰罗…

33 WEB漏洞-逻辑越权之水平垂直越权全解

目录 前言水平&#xff0c;垂直越权&#xff0c;未授权访问Pikachu-本地水平垂直越权演示(漏洞成因)墨者水平-身份认证失效漏洞实战(漏洞成因)原理越权检测-Burpsuite插件Authz安装测试(插件使用)修复防御方案 前言 越权漏洞文章分享&#xff1a;https://www.cnblogs.com/zhen…