zoukankan      html  css  js  c++  java
  • 设计模式之工厂模式(Factory Pattern)

    一.什么是工厂模式?

    1.“简单工厂模式”,Simple Factory Pattern

    也就是常用的在Factory类中定义静态方法负责new对象的方式。

    摘要中提到过“严格地说,这种被称为“简单工厂模式”的方式根本不能称之为“模式””,虽然静态工厂方法并不是真正的“设计模式”,但这种方式的应用也很广泛,也能带来一些好处,所以我们不能因为它不是“设计模式”就抛弃它

    2.工厂方法模式,Factory Method Pattern

    工厂模式的核心

    定义一个抽象的“工厂方法”来负责new对象,由该类的扩展类来实现如何new的过程

    这种方式成功分离了对象创建过程与对象行为(暂不做解释),确切地说是把对象创建“下放”到了子类。(注意这里的动词——“下放”)

    3.抽象工厂模式,Abstract Factory Pattern

    对工厂方法模式的一种应用与扩展

    把多个“工厂方法”封装在一个抽象工厂类中,以实现“new一组对象”的目的

    二.一般方式(不用工厂模式)

    我们来开一个小店卖衣服,首先需要ColthesStore类负责接收订单返回做好的衣服,ClothesStore类需要依赖Clothes类,毕竟要操作的具体对象是Clothes,ClothesStore中还要有做衣服的工序(制作、加工装饰。。)

    那么我们的ClothesStore将是这样的:

    package FactoryPattern;
    
    /**
     * @author ayqy
     * 不使用工厂模式,直接创建具体对象
     *
     */
    public class ClothesStore {
    	
    	String type;//定义衣服类型
    	Clothes clothes;//衣服对象
    	
    	/**
    	 * @author ayqy
    	 * 定义内部类Clothes
    	 *
    	 */
    	class Clothes{
    		String type;//类型
    		String desc;//描述
    		String cloth;//布料
    		
    		public Clothes(String type, String desc, String cloth){
    			this.type = type;
    			this.desc = desc;
    			this.cloth = cloth;
    		}
    		
    		public String toString(){
    			return type+ "," + desc + "," + cloth;
    		}
    	}
    	
    	public ClothesStore(String type){
    		this.type = type;
    	}
    	
    	/**
    	 * @return 返回未经装饰加工的衣服
    	 */
    	private Clothes createClothes(String type){
    		if(type.equals("外套")){
    			return new Clothes("外套", "一件好看的外套", "麻布");
    		}else if(type.equals("内衣")){
    			return new Clothes("内衣", "一件舒适的内衣", "棉布");
    		}else
    			return null;
    	}
    	
    	/**
    	 * @return 返回做好的衣服
    	 */
    	public Clothes getClothes(){
    		clothes = createClothes(type);
    		decorate(clothes);//对衣服进行装饰
    		
    		return clothes;
    	}
    	
    	private void decorate(Clothes clothes){
    		//给衣服添加装饰(图案、纹样等等)
    		System.out.println("精心装饰完毕,衣服变得更漂亮了");
    	}
    }
    

    P.S.代码中把Clothes定义为内部类,只是为了节省篇幅简化结构,效果一样

    我们发现ClothesStore对Clothes有着很强的依赖(过分依赖“具体”可不是一件好事儿。。),一旦Clothes发生变化(如有了新的成员变量与成员函数),我们的ClothesStore可能就不得不跟着修改了,尤其是负责new衣服对象的部分:

    /**
    * @return 返回未经装饰加工的衣服
    */
    private Clothes createClothes(String type){
    if(type.equals("外套")){
    		return new Clothes("外套", "一件好看的外套", "麻布");
    	}else if(type.equals("内衣")){
    		return new Clothes("内衣", "一件舒适的内衣", "棉布");
    	}else
    		return null;
    }
    

    一旦有了新的type我们就要添加一个else if来应对变化

    而且由于ClothesStore与具体的Clothes类绑定在一起,导致ClothesStore不易于扩展,很难复用

    三.“简单工厂模式”

    把createClothes方法移动到ClothesFactory中作为一个静态工厂方法,就像这样:

    package FactoryPattern.SimpleFactoryPattern;
    
    /**
     * @author ayqy
     * 定义静态工厂方法
     *
     */
    public class ClothesFactory {
    	/**
    	 * @return 返回未经装饰加工的衣服
    	 */
    	public static Clothes createClothes(String type){
    		if(type.equals("外套")){
    			return new Clothes("外套", "一件好看的外套", "麻布");
    		}else if(type.equals("内衣")){
    			return new Clothes("内衣", "一件舒适的内衣", "棉布");
    		}else
    			return null;
    	}
    }
    

    P.S.这次必须把Clothes拿出来作为一个独立的类了(内部类无法访问),除此之外,ClothesStore中的getClothes方法也要做相应改变。。不要在意这些细节

    应用简单工厂模式之后,我们确实把new对象的部分独立出来了,这样做的好处是:

    当Clothes发生变化时,我们可以在ClothesFactory中直接修改create方法,而不是在ClothesStore的长篇代码中寻找Create部分

    而且与之前的“一般方式”对比,代码几乎没有发生什么变化,很容易把之前的代码改成应用简单工厂模式之后的代码,所以很多开发人员喜欢这种方式

    -------

    “简单工厂模式”并不是真正的工厂模式,它的优势也是极其有限的,不过没关系,我们还有工厂方法模式,这次是货真价实的工厂模式

    四.工厂方法模式

    定义抽象的“工厂方法”,负责具体实现,全新的ClothesStore将是这样的:

    package FactoryPattern.FactoryMethodPattern;
    
    /**
     * @author ayqy
     * 使用工厂方法模式,定义抽象的工厂方法来new产品
     *
     */
    public abstract class ClothesStore {
    	
    	String type;//定义衣服类型
    	Clothes clothes;//衣服对象
    	
    	public ClothesStore(String type){
    		this.type = type;
    	}
    	
    	//定义工厂方法,由扩展类来实现具体制作细节
    	public abstract Clothes createClothes(String type);
    	
    	/**
    	 * @return 返回做好的衣服
    	 */
    	public Clothes getClothes(){
    		clothes = createClothes(type);
    		decorate(clothes);//对衣服进行装饰
    		
    		return clothes;
    	}
    	
    	private void decorate(Clothes clothes){
    		//给衣服添加装饰(图案、纹样等等)
    		System.out.println("精心装饰完毕,衣服变得更漂亮了");
    	}
    }
    

    我们发现ClothesStore变成Abstract的了,里面封装了衣服的制作工序以及实现细节,但并没有实现Create过程

    下一步是要扩展ClothesStore类,实现具体Create细节,就像这样:

    package FactoryPattern.FactoryMethodPattern;
    
    /**
     * @author ayqy
     * 扩展ClothesStore,实现具体制作细节
     *
     */
    public class DefaultClothesStore extends ClothesStore{
    
    	public DefaultClothesStore(String type) {
    		super(type);
    	}
    
    	/* 
    	 * 实现具体制作细节
    	 */
    	@Override
    	public Clothes createClothes(String type) {
    		if(type.equals("外套")){
    			return new Clothes("外套", "一件好看的外套", "麻布");
    		}else if(type.equals("内衣")){
    			return new Clothes("内衣", "一件舒适的内衣", "棉布");
    		}else
    			return null;
    	}
    }
    

    嗯,现在我们已经成功应用工厂方法模式了,让我们来看看相比之前的简单工厂模式,这个货真价实的工厂模式有哪些特点

    1.同样实现了new对象的具体过程与对象行为的分离,但不同的是我们是利用继承(扩展)来实现的,也就是开篇提到的“下放”(把具体实现放到更低的类层次上)

    2.每一个具体Store都必须实现自己的Create细节,但同时又可以利用基类Store的制作工艺(decorate方法等等)

    3.一个抽象的工厂方法轻松实现了工厂模式(甚至Factory自始至终根本没有出现,但我们确实已经实现了“工厂”,不是吗?)

    五.抽象工厂模式

    抽象工厂模式是对上一种方式的扩展,在上面我们只能Create一个产品,但很多时候我们需要Create一组产品,比如衣服的原料,包括布料、染料、线、纽扣等等,这时候就需要应用抽象工厂模式来实现:

    package FactoryPattern.AbstractFactoryPattern;
    
    /**
     * @author ayqy
     * 定义原料工厂
     *
     */
    public abstract class ResourcesFactory {
    	public abstract Cloth getCloth();//获取布料
    	public abstract Color getColor();//获取染料
    	public abstract Button getButton();//获取纽扣
    	//需要的其它工厂方法
    }
    

    抽象工厂中的类型都是自定义接口,例如:

    package FactoryPattern.AbstractFactoryPattern;
    
    /**
     * @author ayqy
     * 定义Cloth接口
     */
    public interface Cloth {
    	//属性
    	//行为
    }
    

    有了原料工厂,那么ClothesStore也要做相应的改变来适应这种新的结构:

    package FactoryPattern.AbstractFactoryPattern;
    
    /**
     * @author ayqy
     * 扩展ClothesStore,实现具体制作细节
     *
     */
    public class DefaultClothesStore extends ClothesStore{
    
    	public DefaultClothesStore(String type, ResourcesFactory res) {
    		super(type, res);
    	}
    
    	/* 
    	 * 实现具体制作细节
    	 */
    	@Override
    	public Clothes createClothes(String type) {
    		if(type.equals("外套")){
    			//获取所需原料
    			Cloth cloth = res.getCloth();
    			Color color = res.getColor();
    			Button button = res.getButton();
    			//制作衣服
    			return new Clothes("外套", color.toString() + button.toString(), cloth.toString());
    		}else if(type.equals("内衣")){
    			//获取所需原料
    			Cloth cloth = res.getCloth();
    			Color color = res.getColor();
    			Button button = res.getButton();
    			//制作衣服
    			return new Clothes("内衣", color.toString() + button.toString(), cloth.toString());
    		}else
    			return null;
    	}
    }
    

    现在创建Store对象时需要一个具体ResourcesFactory参数(具体的原料工厂实现了所有的getXXX工厂方法),具体的Store只负责具体制作过程,需要什么原料都可以从ResourceFactory中取得,具体Store做衣服后,由Store基类负责工艺加工(decorate方法等等),最后由具体的Store返回精加工完毕的衣服

    整个结构很疏松,低耦合,易扩展

    -------

    除此之外,还有必要提及一个OO设计原则——“依赖倒置原则

    通过观察类结构我们可以发现:

    高层组件Store依赖Clothes,同时低层组件ResourcesFactory也依赖Clothes(这就是“依赖倒置”,即低层组件反过来依赖高层组件,同时高低层组件都依赖抽象

    这样的设计有着极易扩展的优势(抽象意味着可扩展。。)

    六.总结

    其实开篇第一部分就是总结,这里要强调的是:事物都有两面性,一个好的东西必然存在缺点,设计模式也不例外。

    抽象工厂模式看起来真的不错,但是也存在致命的缺点:多级抽象引起的结构复杂化问题

  • 相关阅读:
    Sqli-labs Less-28a 绕过unions+select过滤 union注入
    eclipse安装freemarker-ide插件
    Eclipse调试时出现source not found的问题
    POJ 1509 Glass Beads 后缀自动机 模板 字符串的最小表示
    1028/3/7 被踩爆的省选模拟赛 30分
    字符串的模板 Manacher kmp ac自动机 后缀数组 后缀自动机
    2018/3/6 省选模拟赛 60分
    埃及分数 a* 搜索 知识点mark
    UOJ #35. 后缀排序 后缀数组 模板
    BZOJ 4566 JZYZOJ 1547 [haoi2016T5]找相同子串 后缀数组 并查集
  • 原文地址:https://www.cnblogs.com/ayqy/p/3960838.html
Copyright © 2011-2022 走看看