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

好菜每回味道不同--建造者模式

1.1 炒菜没放盐

        中餐,老板需要每次炒菜,每次炒出来的味道都有可能不同。麦当劳、肯德基这些不过百年的洋快餐却能在有千年饮食文化的中国发展的那么好呢?是因为你不管何时何地在哪里吃味道都一样,而鱼香肉丝在我们中餐却可以吃出上完口味来。

        依赖倒转原则?抽象不应该依赖细节,细节应该依赖于抽象,由于我们要吃的菜都依赖于厨师这样的细节,所以我们就很被动。

        "好,那再想想,老麦老肯他们的产品,味道是由什么决定的?"
        "我知道,那是由他们的工作流程决定的,由于他们制定了非常规范的工作流程,原料放多少,加热几分钟,都有严格规定,估计放多少盐都是用克来计量的。而这个工作流程是在所有的门店都必须要遵照执行的,所以我们吃到的东西不管在哪在什么时候味道都一样。这里我们要吃的食物都依赖工作流程。不过工作流程好像还是细节呀。"
        "对,工作流程也是细节,我们去快餐店消费,我们用不用关心他们的工作流程?当然是不用,我们更关心的是是否好吃。你想如果老肯发现鸡翅烤得有些焦,他们会调整具体的工作流程中的烧烤时间,如果新加一种汉堡,做法都相同,只是配料不相同,工作流程是不变的,只是加了一种具体产品而已,这里工作流程怎么样?"
        "对,这里工作流程可以是一种抽象的流程,具体放什么配料、烤多长时间等细节依赖于这个抽象。"

1.2 建造小人一

建造小人,要求要有头、身体、两手、两脚就可以了

package code.chapter13.builder1;
import java.awt.Graphics;
import javax.swing.JFrame;class Test extends JFrame {public Test() {setSize(400, 400);setDefaultCloseOperation(EXIT_ON_CLOSE);setLocationRelativeTo(null);}public void paint(Graphics g) {//瘦小人g.drawOval(150, 120, 30, 30);   //头g.drawRect(160, 150, 10, 50);   //身体g.drawLine(160, 150, 140, 200); //左手g.drawLine(170, 150, 190, 200); //右手g.drawLine(160, 200, 145, 250); //左脚g.drawLine(170, 200, 185, 250); //右脚//胖小人g.drawOval(250, 120, 30, 30);   //头g.drawOval(245, 150, 40, 50);   //身体g.drawLine(250, 150, 230, 200); //左手g.drawLine(280, 150, 300, 200); //右手g.drawLine(260, 200, 245, 250); //左脚g.drawLine(270, 200, 285, 250); //右脚}public static void main(String[] args) {new Test().setVisible(true);}
}

这样的话,有可能少画了一条腿或者一条胳膊,就像厨师有可能忘记放盐。

1.3 建造小人二

建两个类,一个廋人的类,一个胖子的类,不管谁都可以调用它

package code.chapter13.builder2;
import java.awt.Graphics;
import javax.swing.JFrame;class Test extends JFrame {public Test() {setSize(400, 400);setDefaultCloseOperation(EXIT_ON_CLOSE);setLocationRelativeTo(null);}public void paint(Graphics g) {//初始化瘦小人建造者类PersonThinBuilder gThin = new PersonThinBuilder(g);gThin.build();//画瘦小人//初始化胖小人建造者类PersonFatBuilder gFat = new PersonFatBuilder(g);gFat.build();//画胖小人}public static void main(String[] args) {new Test().setVisible(true);}
}//瘦小人建造者
class PersonThinBuilder {private Graphics g;public PersonThinBuilder(Graphics g){this.g=g;}public void build(){g.drawOval(150, 120, 30, 30);   //头g.drawRect(160, 150, 10, 50);   //身体g.drawLine(160, 150, 140, 200); //左手g.drawLine(170, 150, 190, 200); //右手g.drawLine(160, 200, 145, 250); //左脚g.drawLine(170, 200, 185, 250); //右脚}
}//胖小人建造者
class PersonFatBuilder {private Graphics g;public PersonFatBuilder(Graphics g){this.g=g;}public void build(){g.drawOval(250, 120, 30, 30);   //头g.drawOval(245, 150, 40, 50);   //身体g.drawLine(250, 150, 230, 200); //左手g.drawLine(280, 150, 300, 200); //右手g.drawLine(260, 200, 245, 250); //左脚g.drawLine(270, 200, 285, 250); //右脚}
}

如果再增加一个高个子的小人,也有可能不小心,最好的办法是规定,凡是建造小人,都必须要有头和身体,以及两手两脚。

1.4 建造者模式

        如果你需要将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示的意图时,我们需要应用于一个设计模式,'建造者模式(Builder)',又叫生成器模式。建造者模式可以将一个产品的内部表象与产品的生成过程分割开来,从而可以使一个建造过程生成具有不同的内部表象的产品对象。如果我们用了建造者模式,那么用户就只需指定需要建造的类型就可以得到它们,而具体建造的过程和细节就不需要知道了。

        建造者模式(Builder),将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。[DP]
        "那怎么用建造者模式呢?"
        "一步一步来,首先我们要画小人,都需要画什么?"
        "头、身体、左手、右手、左脚、右脚。"
        "对的,所以我们先定义一个抽象的建造人的类,来把这个过程给稳定住,不让任何人遗忘当中的任何一步。"

        "然后,我们需要建造一个瘦的小人,则让这个瘦子类去继承这个抽象类,那就必须去重写这些抽象方法了。否则编译器也不让你通过。"
        "当然,胖人或高个子其实都是用类似的代码去实现这个类就可以了。"
        "这样,我在客户端要调用时,还是需要知道头身手脚这些方法呀?没有解决问题。"小菜不解地问。
        "别急,我们还缺建造者模式中一个很重要的类,指挥者(Director),用它来控制建造过程,也用它来隔离用户与建造过程的关联。"
        "你看到没有,PersonDirector类的目的就是根据用户的选择来一步一步建造小人,而建造的过程在指挥者这里完成了,用户就不需要知道了,而且,由于这个过程每一步都是一定要做的,那就不会让少画了一只手,少画一条腿的问题出现了。"
        "代码结构图如下。"

package code.chapter13.builder3;
import java.awt.Graphics;
import javax.swing.JFrame;class Test extends JFrame {public Test() {setSize(400, 400);setDefaultCloseOperation(EXIT_ON_CLOSE);setLocationRelativeTo(null);}public void paint(Graphics g) {PersonBuilder gThin = new PersonThinBuilder(g);PersonDirector pdThin = new PersonDirector(gThin);pdThin.CreatePerson();PersonBuilder gFat = new PersonFatBuilder(g);PersonDirector pdFat = new PersonDirector(gFat);pdFat.CreatePerson();}public static void main(String[] args) {new Test().setVisible(true);}
}//抽象的建造者类
abstract class PersonBuilder {protected Graphics g;public PersonBuilder(Graphics g){this.g = g;}public abstract void buildHead();       //头public abstract void buildBody();       //身体public abstract void buildArmLeft();    //左手public abstract void buildArmRight();   //右手public abstract void buildLegLeft();    //左脚public abstract void buildLegRight();   //右脚
}//瘦小人建造者
class PersonThinBuilder extends PersonBuilder {public PersonThinBuilder(Graphics g){super(g);}public void buildHead(){g.drawOval(150, 120, 30, 30);   //头}public void buildBody(){g.drawRect(160, 150, 10, 50);   //身体}public void buildArmLeft(){g.drawLine(160, 150, 140, 200); //左手}public void buildArmRight(){g.drawLine(170, 150, 190, 200); //右手}public void buildLegLeft(){g.drawLine(160, 200, 145, 250); //左脚}public void buildLegRight(){g.drawLine(170, 200, 185, 250); //右脚 }
}//胖小人建造者
class PersonFatBuilder extends PersonBuilder {public PersonFatBuilder(Graphics g){super(g);}public void buildHead(){g.drawOval(250, 120, 30, 30);   //头}public void buildBody(){g.drawOval(245, 150, 40, 50);   //身体}public void buildArmLeft(){g.drawLine(250, 150, 230, 200); //左手}public void buildArmRight(){g.drawLine(280, 150, 300, 200); //右手}public void buildLegLeft(){g.drawLine(260, 200, 245, 250); //左脚}public void buildLegRight(){g.drawLine(270, 200, 285, 250); //右脚}
}//指挥者
class PersonDirector{private PersonBuilder pb;//初始化时指定需要建造什么样的小人public PersonDirector(PersonBuilder pb){this.pb=pb;}//根据用户的需要建造小人public void CreatePerson(){pb.buildHead();     //头pb.buildBody();     //身体pb.buildArmLeft();  //左手pb.buildArmRight(); //右手pb.buildLegLeft();  //左脚pb.buildLegRight(); //右脚}
}


        "哈,我明白了,那客户端的代码我来写吧。应该也不难实现了。"
        "试想一下,我如果需要增加一个高个子和矮个子的小人,我们应该怎么做?"
        "加两个类,一个高个子类和一个矮个子类,让它们都去继承PersonBuilder,然后客户端调用就可以了。但我有个问题,如果我需要细化一些,比如人的五官,手的上臂、前臂和手掌,大腿小腿这些,如何办呢?"
        "问得好,这就需要权衡,如果这些细节是每个具体的小人都需要构建的,那就应该要加进去,反之就没必要。其实建造者模式是逐步建造产品的,所以建造者的Builder类里的那些建造方法必须要足够普遍,以便为各种类型的具体建造者构造。"

1.5 建造者解析

建造者模式(Builder)结构图

        "现在你看这张图就不会感觉陌生了。来总结一下,Builder是什么?"
        "是一个建造小人各个部分的抽象类。"
        "概括地说,是为创建一个Product对象的各个部件指定的抽象接口。ConcreteBuilder是什么呢?"
        "具体的小人建造者,具体实现如何画出小人的头身手脚各个部分。"
        "对的,它是具体建造者,实现Builder接口,构造和装配各个部件。Product当然就是那些具体的小人,产品角色了,Director是什么?"
        "指挥者,用来根据用户的需求构建小人对象。"
        "嗯,它是构建一个使用Builder接口的对象。"
        "那都是什么时候需要使用建造者模式呢?"
        "它主要用于创建一些复杂的对象,这些对象内部子对象的建造顺序通常是稳定的,但每个子对象本身的构建通常面临着复杂的变化。"
        "哦,是不是建造者模式的好处就是使得建造代码与表示代码分离,由于建造者隐藏了该产品是如何组装的,所以若需要改变一个产品的内部表示,只需要再定义一个具体的建造者就可以了。"
        "来来来,我们来试着把建造者模式的基本代码推演一下,以便有一个更宏观的认识。"

1.6 建造者模式基本代码

        Product类——产品类,由多个部件组成。

        Builder类——抽象建造者类,确定产品由两个部件PartA和PartB组成,并声明一个得到产品建造后结果的方法GetResult。
        ConcreteBuilder1类——具体建造者类。
        ConcreteBuilder2类——具体建造者类。
        Director类——指挥者类。
        客户端代码,客户不需要知道具体的建造过程。

package code.chapter13.builder0;import java.util.ArrayList;public class Test {public static void main(String[] args){System.out.println("**********************************************");       System.out.println("《大话设计模式》代码样例");System.out.println();  Director director = new Director();Builder b1 = new ConcreteBuilder1();Builder b2 = new ConcreteBuilder2();//指挥者用ConcreteBuilder1的方法来建造产品director.construct(b1); //创建的是产品A和产品BProduct p1 = b1.getResult();p1.show();//指挥者用ConcreteBuilder2的方法来建造产品director.construct(b2); //创建的是产品X和产品YProduct p2 = b2.getResult();p2.show();System.out.println();System.out.println("**********************************************");}
}//产品类
class Product{ArrayList<String> parts = new ArrayList<String>();//添加新的产品部件public void add(String part){parts.add(part);}//列举所有产品部件public void show(){for(String part : parts){System.out.println(part);}}
}//抽象的建造者类
abstract class Builder {public abstract void buildPartA();      //建造部件Apublic abstract void buildPartB();      //建造部件Bpublic abstract Product getResult();    //得到产品
}//具体建造者1
class ConcreteBuilder1 extends Builder {private Product product = new Product();public void buildPartA(){product.add("部件A");}public void buildPartB(){product.add("部件B");}public Product getResult(){return product;}
}//具体建造者2
class ConcreteBuilder2 extends Builder {private Product product = new Product();public void buildPartA(){product.add("部件X");}public void buildPartB(){product.add("部件Y");}public Product getResult(){return product;}
}//指挥者
class Director{public void construct(Builder builder){builder.buildPartA();builder.buildPartB();}
}


        "所以说,建造者模式是在当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时适用的模式。"

相关文章:

好菜每回味道不同--建造者模式

1.1 炒菜没放盐 中餐&#xff0c;老板需要每次炒菜&#xff0c;每次炒出来的味道都有可能不同。麦当劳、肯德基这些不过百年的洋快餐却能在有千年饮食文化的中国发展的那么好呢&#xff1f;是因为你不管何时何地在哪里吃味道都一样&#xff0c;而鱼香肉丝在我们中餐却可以吃出上…...

RuoYi-Cloud下载与运行

一、源码下载 若依官网:RuoYi 若依官方网站 鼠标放到"源码地址"上,点击"RuoYi-Cloud 微服务版"。 跳转至Gitee页面,点击"克隆/下载",复制HTTPS链接即可。 源码地址为:https://gitee.com/y_project/RuoYi-Cloud.git 点击复制 打开IDEA,选…...

Vue2.x计算属性

1.计算属性 在Vue 插值表达式内实现一些操作其实非常便利&#xff0c;但如果表达式的逻辑过于复杂&#xff0c;会让插值过于臃肿且难以维护。这时可以考虑使用Vue的计算属性 1.1 不使用计算属性的例子 <!DOCTYPE html> <html><head><meta charset"…...

Vue中使用require.context()自动引入组件和自动生成路由的方法介绍

目录 一、自动引入组件 1、语法 2、使用 2.1、在compoents文件下随便创建index.js文件 2.2、mian.js引入该js 二、自动生成路由 1、示例&#xff1a; 2、使用 2.1、在router文件下随便创建autoRouter.js文件 2.2、在router文件下index.js文件中引入autoRouter.js文件…...

【炒股Zero To Hero】MACD金叉死叉到底是否有效,加上这个指标回报率增加197倍

移动平均收敛散度&#xff08;MACD - Moving Average Convergence Divergence&#xff09;是一种趋势跟踪动量指标&#xff0c;显示了证券价格的两个移动平均之间的关系。它用于识别趋势的方向和强度&#xff0c;属于技术分析中振荡器的一类。 MACD如何衡量股票及其趋势 有两…...

Linux网络名称空间和虚拟机有何区别

在Linux系统中&#xff0c;网络名称空间和虚拟机都是实现资源隔离和虚拟化的技术&#xff0c;但它们在设计理念、实现机制、资源消耗、使用场景等方面存在着显著的区别。本文旨在全方位、系统性地分析这两种技术的区别。&#x1f50d; 1. 设计理念与实现机制 1.1. 网络名称空…...

【UE Niagara】蓝图获取粒子数据

目录 效果 步骤 一、创建粒子 二、创建蓝图接收Niagara参数 效果 步骤 一、创建粒子 1. 新建一个Niagara发射器&#xff0c;使用Empty模板&#xff0c;打开后先添加“Spawn Rate”模块&#xff0c;这里设置粒子生成速率为0.7 在“Initialize Particle”模块中设置粒子颜色…...

更改el-cascade默认的value和label的键值

后端返回的树结构中&#xff0c;label的key不是el-cascade默认的label&#xff0c;我需要改成对应的字段&#xff0c;但是一直没有成功&#xff0c;我也在文档中找到了说明&#xff0c;但是我没注意这是在props中改&#xff0c;导致一直不成功 这是我一开始错误的写法&#xf…...

2024邮件工单系统排行揭晓:出海必备新宠

2024年各大榜单结果纷纷出炉&#xff0c;一起来看看2024十大邮件工单系统最新排行吧&#xff01; 2024十大邮件工单系统 1、Zoho Desk&#xff1b;2、FreshDesk&#xff1b;3、Service Desk Plus&#xff1b;4、Help Scout&#xff1b;5、Helpshift&#xff1b;6、HongDans&am…...

java题目17:以m行n列二维数组为参数进行方法调用,分别计算二维数组各列元素之和,返回并输出计算结果(MethodCalls17)

每日小语 伟大企业的一项特质是“利润之上的追求”。——段永平 思考 方法调用 方法调用是通过在代码中使用方法名和参数列表来实现的。 public class MethodExample {public static void main(String[] args) {// 调用方法add&#xff0c;并传入两个参数int sum add(3, 5…...

Python中Python-docx 包的run介绍

先对run做一个简单地介绍。每个paragraph对象都包含一个run对象的列表。举例&#xff1a; 这是一个简短的段落。 from docx import Document doc Document("1.docx") #上面这段话保存在1.docx中 print("这一段的run个数是&#xff1a;",len(doc.paragr…...

vue2升级到vue3的一些使用注意事项记录(三)

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 http://122.227.135.243:9666/ 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a…...

SwiftUI Swift 显示隐藏系统顶部状态栏

Show me the code // // TestHideSystemTopBar.swift // pandabill // // Created by 朱洪苇 on 2024/4/1. //import SwiftUIstruct TestHideSystemTopBar: View {State private var isStatusBarHidden falsevar body: some View {Button {withAnimation {self.isStatusBa…...

PowerJob 分布式任务调度简介

目录 适用场景 设计目标 PowerJob 功能全景 任务调度 工作流 分布式计算 动态容器 什么是动态容器? 使用场景 可维护性和灵活性的完美结合 实时日志&在线运维 PowerJob 系统组件 PowerJob 应用场景 PowerJob 的优势 PowerJob&#xff08;原OhMyScheduler&…...

Java——数组练习

目录 一.数组转字符串 二.数组拷贝 三.求数组中元素的平均值 四.查找数组中指定元素(顺序查找) 五.查找数组中指定元素(二分查找) 六.数组排序(冒泡排序) 七.数组逆序 一.数组转字符串 代码示例&#xff1a; import java.util.Arrays int[] arr {1,2,3,4,5,6}; String…...

波士顿房价预测案例(python scikit-learn)---多元线性回归(多角度实验分析)

波士顿房价预测案例&#xff08;python scikit-learn&#xff09;—多元线性回归(多角度实验分析) 这次实验&#xff0c;我们主要从以下几个方面介绍&#xff1a; 一、相关框架介绍 二、数据集介绍 三、实验结果-优化算法对比实验&#xff0c;数据标准化对比实验&#xff0…...

在 Queue 中 poll()和 remove()有什么区别?

在Java的Queue接口中&#xff0c;poll()和remove()方法都用于从队列中删除并返回队列的头部元素&#xff0c;但是它们在队列为空时的行为有所不同。 poll()方法&#xff1a;当队列为空时&#xff0c;poll()方法会返回null&#xff0c;而不会抛出异常。这是它的主要特点&#x…...

实现鼠标在页面点击出现焦点及大十字星

近段时间&#xff0c;在完成项目进度情况显示时候&#xff0c;用户在操作鼠标时候&#xff0c;显示当鼠标所在位置对应时间如下图所示 代码实现步骤如下&#xff1a; 1.首先引用 jquery.1.7.js 2.再次引用raphael.js 3.然后引用graphics.js 4.最后引用mfocus.js 其中mfocu…...

如何在 7 天内掌握C++?

大家好&#xff0c;我是小康&#xff0c;今天我们来聊下如何快速学习 C 语言。 本篇文章适合于有 C 语言编程基础的小伙伴们&#xff0c;如果还没有学习过 C&#xff0c;请看这篇文章先入个门&#xff1a;C语言快速入门 引言&#xff1a; C&#xff0c;作为一门集面向过程和…...

FineBI概述

FineBI是一种商业智能&#xff08;BI&#xff09;软件&#xff0c;旨在帮助企业从数据中获取见解并做出更明智的业务决策。以下是FineBI的详细概述&#xff1a; 功能特性&#xff1a; 数据连接与整合&#xff1a;FineBI可以连接到各种数据源&#xff0c;包括数据库、数据仓库、…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…...

智慧医疗能源事业线深度画像分析(上)

引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》

在注意力分散、内容高度同质化的时代&#xff0c;情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现&#xff0c;消费者对内容的“有感”程度&#xff0c;正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中&#xff0…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢

随着互联网技术的飞速发展&#xff0c;消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁&#xff0c;不仅优化了客户体验&#xff0c;还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用&#xff0c;并…...

【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例

文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...

剑指offer20_链表中环的入口节点

链表中环的入口节点 给定一个链表&#xff0c;若其中包含环&#xff0c;则输出环的入口节点。 若其中不包含环&#xff0c;则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...

Nuxt.js 中的路由配置详解

Nuxt.js 通过其内置的路由系统简化了应用的路由配置&#xff0c;使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...

C++八股 —— 单例模式

文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全&#xff08;Thread Safety&#xff09; 线程安全是指在多线程环境下&#xff0c;某个函数、类或代码片段能够被多个线程同时调用时&#xff0c;仍能保证数据的一致性和逻辑的正确性&#xf…...

libfmt: 现代C++的格式化工具库介绍与酷炫功能

libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库&#xff0c;提供了高效、安全的文本格式化功能&#xff0c;是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全&#xff1a…...

书籍“之“字形打印矩阵(8)0609

题目 给定一个矩阵matrix&#xff0c;按照"之"字形的方式打印这个矩阵&#xff0c;例如&#xff1a; 1 2 3 4 5 6 7 8 9 10 11 12 ”之“字形打印的结果为&#xff1a;1&#xff0c;…...