前言
在上一篇 https://www.cnblogs.com/xuwujing/p/9363142.html 中我们学习了工厂模式, 介绍了简单工厂模式, 工厂方法和抽象工厂模式. 本篇则介绍设计模式中属于创建型模式的建造者模式和原型模式.
建造者模式
简介
建造者模式是属于创建型模式. 建造者模式使用多个简单的对象一步一步构建成一个复杂的对象. 这种类型的设计模式属于创建型模式, 它提供了一种创建对象的最佳方式. 简单的来说就是将一个复杂的东西抽离出来, 对外提供一个简单的调用, 可以在同样的构建过程创建不同的表示. 和工厂模式很相似, 不过相比而言更加注重组件的装配.
这里用一个示例来进行说明. 我们一天吃的食物有这些, 煎饼, 盒饭, 拉面, 豆浆, 牛奶和果汁. 分为三餐, 早餐, 午餐和晚餐, 餐点主要包含吃的 (俗称饭) 和喝的(豆浆, 果汁之类的), 那么我们可以把煎饼和豆浆作为早餐, 盒饭和果汁作为午餐, 这样我们可以清楚的知道要吃早餐和午餐包含什么食物.
首先我们定义一个食物类, 有两个属性, 吃的和喝的.
- class Meal{
- private String food;
- private String drinks;
- public String getFood() {
- return food;
- }
- public void setFood(String food) {
- this.food = food;
- }
- public String getDrinks() {
- return drinks;
- }
- public void setDrinks(String drinks) {
- this.drinks = drinks;
- }
- }
复制代码
定义了食物时候, 我们在定义一个食物的标准接口, 一份食物包含什么, 其实也就是吃的和喝的.
- interface IBuilderFood{
- void buildFood();
- void buildDrinks();
- Meal createMeal();
- }
复制代码
食物接口定义一个吃的和一个喝的组件, 然后通过 **createMeal()** 方法返回我们需要的食物. 那么现在我们便可以定义一份早餐和午餐. 代码示例:
- class Breakfast implements IBuilderFood{
- Meal meal;
- public Breakfast(){
- meal=new Meal();
- }
- @Override
- public void buildFood() {
- meal.setFood("煎饼");
- }
- @Override
- public void buildDrinks() {
- meal.setDrinks("豆浆");
- }
- @Override
- public Meal createMeal() {
- return meal;
- }
- }
- class Lunch implements IBuilderFood{
- Meal meal;
- public Lunch(){
- meal=new Meal();
- }
- @Override
- public void buildFood() {
- meal.setFood("盒饭");
- }
- @Override
- public void buildDrinks() {
- meal.setDrinks("果汁");
- }
- @Override
- public Meal createMeal() {
- return meal;
- }
- }
复制代码
定义完之后, 建造早餐和午餐的的过程已经完毕了. 但是这并不是建造者模式, 它有个核心的 Director(导演者), 它用来创建复杂对象的部分, 对该部分进行完整的创建或者按照一定的规则进行创建. 那么这里我们可以创建一个 Director, 用来创建一份餐点. 至于创建的是什么餐点, 它不用知道, 这一点由调用者来进行决定.
这里我们就可以定义一个饭店, 可以创建一份餐点, 创建什么餐点有顾客决定. 代码示例:
- class FoodStore{
- public Meal createBreakfast(IBuilderFood bf){
- bf.buildDrinks();
- bf.buildFood();
- return bf.createMeal();
- }
- }
复制代码
创建完成这个 Director 之后, 我们再来进行调用测试.
代码示例:
- public class BuilderTest {
- public static void main(String[] args) {
- FoodStore foodStore=new FoodStore();
- Meal meal=foodStore.createBreakfast(new Breakfast());
- Meal meal2=foodStore.createBreakfast(new Lunch());
- System.out.println("小明早上吃的是:"+meal.getFood()+", 喝的饮料是:"+meal.getDrinks());
- System.out.println("小明中午吃的是:"+meal2.getFood()+", 喝的饮料是:"+meal2.getDrinks());
- }
- }
复制代码
输出结果:
小明早上吃的是: 煎饼, 喝的饮料是: 豆浆
小明中午吃的是: 盒饭, 喝的饮料是: 果汁
复制代码
简单的介绍了下建造者模式的运作原理, 可以概况为这 4 点:
Builder: 指定一个抽象的接口, 规定该产品所需实现部件的创建, 并不涉及具体的对象部件的创建.
ConcreteBuilder: 需实现 Builder 接口, 并且针对不同的逻辑, 进行不同方法的创建, 最终提供该产品的实例.
Director: 用来创建复杂对象的部分, 对该部分进行完整的创建或者按照一定的规则进行创建.
Product: 示被构造的复杂对象.
使用场景: 适用一些基本组件不便, 但是组合经常变化的时候. 比如超市促销的大礼包.
优点:
建造者独立, 易扩展.
便于控制细节风险.
缺点
内部结构复杂, 不易于理解.
产品直接需要有共同点, 范围有控制.
原型模式
原型模式 (Prototype Pattern) 是用于创建重复的对象, 同时又能保证性能. 这种类型的设计模式属于创建型模式, 它提供了一种创建对象的最佳方式.
一般来说我们在创建对象的时候是直接创建的, 但是创建该对象的代价很大的时候, 重复的二次创建就有些不划算, 这时我们就可以使用原型模式. 打个比方, 我们都发送过邮件, 在节日的时候一般发送的是祝福语句, 在这些祝福语句中, 一般除了名字不一样之外, 大部分都是一样的. 这时我们就可以利用该模式来进行相应出创建.
这里还是用一个的简单的示例来说明. 小明和小红在同一天生日, 然后我们需要给他们发送邮件进行祝福, 但是由于比较懒, 祝福语除了名字之外都是一样的. 这时我们就可以先完成祝福语的编写, 然后克隆该祝福语, 最后根据不同的名称进行发送. 不过这里就从简了, 只是简单的打印下而已.
代码示例:
- public class PrototypeTest {
- public static void main(String[] args) {
- Mail mail=new Mail();
- mail.setMsg("生日快乐!");
- Mail mail2=(Mail) mail.clone();
- mail.setName("小明");
- mail2.setName("小红");
- System.out.println(mail.toString());
- System.out.println(mail2.toString());
- }
- }
- class Mail implements Cloneable {
- private String name;
- private String msg;
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public String getMsg() {
- return msg;
- }
- public void setMsg(String msg) {
- this.msg = msg;
- }
- public Object clone() {
- Object clone = null;
- try {
- clone = super.clone();
- } catch (CloneNotSupportedException e) {
- e.printStackTrace();
- }
- return clone;
- }
- @Override
- public String toString() {
- return name + ":" + msg ;
- }
- }
复制代码
输出结果:
小明: 生日快乐!
小红: 生日快乐!
复制代码
看完原型模式的创建, 是不是感觉就是和 Java 中克隆即为类似呢? 实际上它的核心也就是克隆. 克隆有两种, 浅克隆和深克隆, 本文主要介绍的是浅克隆. 浅克隆:
在浅克隆中, 如果原型对象的成员变量是值类型, 将复制一份给克隆对象; 如果原型对象的成员变量是引用类型, 则将引用对象的地址复制一份给克隆对象, 也就是说原型对象和克隆对象的成员变量指向相同的内存地址. 简单来说, 在浅克隆中, 当对象被复制时只复制它本身和其中包含的值类型的成员变量, 而引用类型的成员对象并没有复制. 实现 Cloneable 接口并重写 Object 类中的 clone()方法;
深克隆:
在深克隆中, 无论原型对象的成员变量是值类型还是引用类型, 都将复制一份给克隆对象, 深克隆将原型对象的所有引用对象也复制一份给克隆对象.
简单来说, 在深克隆中, 除了对象本身被复制外, 对象所包含的所有成员变量也将复制. 实现 Serializable 接口, 通过对象的序列化和反序列化实现克隆, 可以实现真正的深度克隆.
使用场景:
类初始化的时候需要消耗大量资源的时候;
获取数据库连接繁琐的时候;
一个对象, 有很多个修改者的时候;
优点: 1. 可以提升性能;
缺点: 1. 因为必须实现 Cloneable 接口, 所以用起来可能不太方便.
来源: https://juejin.im/post/5b77818351882542fb6f465a