zoukankan      html  css  js  c++  java
  • Java进阶篇设计模式之三 ----- 建造者模式和原型模式

    前言

    上一篇中我们学习了工厂模式,介绍了简单工厂模式、工厂方法和抽象工厂模式。本篇则介绍设计模式中属于创建型模式的建造者模式和原型模式。

    建造者模式

    简介

    建造者模式是属于创建型模式。建造者模式使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
    简单的来说就是将一个复杂的东西抽离出来,对外提供一个简单的调用,可以在同样的构建过程创建不同的表示。和工厂模式很相似,不过相比而言更加注重组件的装配。

    这里用一个示例来进行说明。
    我们一天吃的食物有这些,煎饼、盒饭、拉面、豆浆、牛奶和果汁。分为三餐、早餐、午餐和晚餐,餐点主要包含吃的(俗称饭)和喝的(豆浆,果汁之类的),那么我们可以把煎饼和豆浆作为早餐,盒饭和果汁作为午餐,这样我们可以清楚的知道要吃早餐和午餐包含什么食物。

    首先我们定义一个食物类,有两个属性,吃的和喝的。

    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点:

    1. Builder:指定一个抽象的接口,规定该产品所需实现部件的创建,并不涉及具体的对象部件的创建。

    2. ConcreteBuilder:需实现Builder接口,并且针对不同的逻辑,进行不同方法的创建,最终提供该产品的实例。

    3. Director:用来创建复杂对象的部分,对该部分进行完整的创建或者按照一定的规则进行创建。

    4. Product:示被构造的复杂对象。

    使用场景:
    适用一些基本组件不便,但是组合经常变化的时候。比如超市促销的大礼包。

    优点:

    1. 建造者独立,易扩展。
    2. 便于控制细节风险。

    缺点

    1. 内部结构复杂,不易于理解。
    2. 产品直接需要有共同点,范围有控制。

    原型模式

    原型模式(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. 类初始化的时候需要消耗大量资源的时候;
    2. 获取数据库连接繁琐的时候;
    3. 一个对象,有很多个修改者的时候;

    优点:
    1.可以提升性能;

    缺点:
    1.因为必须实现Cloneable 接口,所以用起来可能不太方便。

    其它

    音乐推荐

    原创不易,如果感觉不错,希望给个推荐!您的支持是我写作的最大动力!
    版权声明:
    作者:虚无境
    博客园出处:http://www.cnblogs.com/xuwujing
    CSDN出处:http://blog.csdn.net/qazwsxpcm    
    个人博客出处:http://www.panchengming.com
    原创不易,转载请标明出处,谢谢!

  • 相关阅读:
    css报模块没找到 分析思路 从后往前找,先定位最后blue.less 解决:iview升级4.0 css没改导致编译不过去
    将config从内部移动到外部 3部曲
    iviewadmin url 加入 Router base #viewDesignAdmin
    phpStudy
    rimraf node_modules 突然不能用了 怀疑是yarn的问题,从环境变量将yarn删掉,能用了
    this.current = params.page || 1 (前提是params对象一定要存在)
    onOK Modal.warning iview 要写一个函数 套上,不然会得不到异步调用,直接弹出的时候就执行了
    ant-design-pro 如何打包成 本地html,双击即可查看
    iview mock main.js
    svelte & Polymer Project
  • 原文地址:https://www.cnblogs.com/xuwujing/p/9496346.html
Copyright © 2011-2022 走看看