zoukankan      html  css  js  c++  java
  • 设计模式-工厂方法 简单工厂 抽象工厂 模板方法

    简单工厂模式:
    创建多种不同类型的产品,根据传入参数的类型进行创建。
    只有一个Creator方法,负责多种不同的产品的创建,传入参数的类型来决定具体创建哪种产品,实现简单,但是如果需要扩展产品的话,就需要修改Creator方法的实现。
    有一个Pizza店,可以生产不同种类的Pizza,对于此代码简单如下:
    //这是一个抽象的pizza,有很多不同种类的pizza,继承于它。

    	class AbstractPizza
    	{
    	public:
    		AbstractPizza(void);
    		virtual ~AbstractPizza(void);
    		virtual void Taste() = 0;
    		int m_nPizzaType;
    	};





    //具体的pizza,继承于抽象pizza。还有其他的多种不同类型的Pizza。

    class SimplePizzaFactory
    	{
    	public:
    		enum PizzaType{
    			CheesePizzaTpe,
    			GreekPizzaType,
    			PipperPizzaType,
    			//..可以继续后续添加
    		};
    		SimplePizzaFactory(void);
    		~SimplePizzaFactory(void);
    		AbstractPizza* CreatePizzaByType(PizzaType type){
    			switch (type)
    			{
    			case SimplePizzaFactory::CheesePizzaTpe:
    				{
    					return new CheesePizza;
    					//if you want, you can do additional initialization for new CheesePizza;
    					//like: 
    					//AbstractPizza* pizza = new CheesePizza;
    					//assert(pizza);
    					//pizza->Initialize();
    				}
    			case SimplePizzaFactory::GreekPizzaType:
    				{
    					return new GreekPizza;
    					//if you want, you can do additional initialization for new CheesePizza;
    				}
    			case SimplePizzaFactory::PipperPizzaType:
    				{
    					return new PipperPizza;
    					//if you want, you can do additional initialization for new CheesePizza;
    				}
    			default:
    				break;
    			}
    		}
    };




    为了我们更好的管理Pizza的创建过程,一般我们会创建一个管理类,它负责创建不同种类的pizza产品。通过SimplePizzaFactory工厂的管理,我们通过传入不同的类型就可以不同种类的pizza产品,外部的调用者只需要传入类型,Creator方法就会返回正确的AbstractPizza的具体类型。甚至于你可以将SimplePizzaFactory定义为单例模式(如果工厂需要数据成员的话)便于以全局变量的方式使用。或者将CreatePizzaByType定义为静态方法(如果没有相关任何数据成员),这样的话就不用创建任何工程对象,直接SimplePizzaFactory::CreatePizzaByType(nPizzaType)就可以,操作更简答。它既然叫简单工厂,确实足够简单,只有一个Creator方法(不过不能叫做工厂方法,注意没有继承),当有新的产品添加进来,必须需要修改Creator方法,以满足新的产品创建的需要,不过说实话这种简单工厂模式对于平常的很多程序都足够。

    class SimplePizzaFactory
    	{
    	public:
    		enum PizzaType{
    			CheesePizzaTpe,
    			GreekPizzaType,
    			PipperPizzaType,
    			//..可以继续后续添加
    		};
    		SimplePizzaFactory(void);
    		~SimplePizzaFactory(void);
    		AbstractPizza* CreatePizzaByType(PizzaType type){
    			switch (type)
    			{
    			case SimplePizzaFactory::CheesePizzaTpe:
    				{
    					return new CheesePizza;
    					//if you want, you can do additional initialization for new CheesePizza;
    					//like: 
    					//AbstractPizza* pizza = new CheesePizza;
    					//assert(pizza);
    					//pizza->Initialize();
    				}
    			case SimplePizzaFactory::GreekPizzaType:
    				{
    					return new GreekPizza;
    					//if you want, you can do additional initialization for new CheesePizza;
    				}
    			case SimplePizzaFactory::PipperPizzaType:
    				{
    					return new PipperPizza;
    					//if you want, you can do additional initialization for new CheesePizza;
    				}
    			default:
    				break;
    			}
    		}
    };





    工厂方法模式:
    对扩展开放,对修改关闭,对于上面的简单工厂,creator方法以后可能需要修改,因此我们可以通过将要改的方面单独提炼出来。这就构造出了工厂方法模式。有一个抽象的工厂方法,它的出现将外部的调用统一化,由具体的工厂负责产品的创建过程。工厂方法模式就是将产品的创建过程延迟到子类,由子类负责具体的产品创建,而且一种工厂只生产一种产品。具体工厂和具体产品一一对应,抽象的Creator方法和抽象的产品对应。
    这经常用在抽象类中,专为创建对象定义一个方法。子类就可以覆盖这个方法来定义要创建的特定对象。通常与模板方法和并使用,模板方法在定义模板算法时一般需要先创建产品,然后再利用产品的方法创建一个流程。

    一个抽象产品类,可以派生出多个具体产品类。 
    一个抽象工厂类,可以派生出多个具体工厂类。 
    每个具体工厂类只能创建一个具体产品类的实例。

    还用上面的例子,现在首先需要一个抽象的Creator方法,你可以把它看成一个抽象工厂,其实也是它是一个特殊的抽象工厂,只生产一种产品.
    不过下面的比普通的工厂方法模式又稍微高级了一点,它是参数化的工厂方法模式,灵活性更大。

    class PizzaFactory
    	{
    	public:
    		enum PizzaType{
    			CheesePizzaTpe,
    			GreekPizzaType,
    			PipperPizzaType,
    		};
    		PizzaFactory(void);
    		~PizzaFactory(void);
    		AbstractPizza* PizzaFactory(PizzaType type)();
    };
    	
    	//具体的工厂,负责生产BeiJing的pizza。
    	class BeiJingPizzaFactory : public PizzaFactory
    	{
    	public:
    
    
    		BeiJingPizzaFactory(void);
    		~BeiJingPizzaFactory(void);
    		AbstractPizza* CreatePizzaByType(PizzaType type)(){
    			switch (type){
    				case PizzaFactory::CheesePizzaTpe:
    					return new BeiJingCheesePizza;
    			case PizzaFactory::GreekPizzaType:
    					return new BeiJingGreekPizza;
    			case PizzaFactory::PipperPizzaType:
    					return new BeiJingPipperPizza;
    			default:
    				return NULL;
    			};
    		}
    	};



    关于BeiJingCheesePizza, BeiJingGreekPizza, BeiJingPipperPizza这些就不写了,就是继承于抽象的Pizza。

    对扩展开放,对修改关闭:
    如果再有天津的,河南的,意大利的等等Pizza。通过使用工厂方法模式,以后即使再有其他地区的pizza店,只需要添加另外的一个具体工厂和具体的产品就行了。不要纠结于增加新的PizzaType怎么办?工厂方法模式专注的点是有新地区的产品,而不是有新的PizzaType。因为最基本的工厂方法模式是一个工厂一个产品,一一对应。如果想着眼于type而忽略地区,可以考虑一种PizzaType一个Pizza工厂,那样添加了新的type,就添加新的工厂和产品就可以了,完美符合对扩展开放,对修改关闭。
    子类实例化:
    工厂方法模式对外部提供的是一个工厂方法,它是一个抽象接口,这样外部依赖的就是抽象而不是具体,减少代码耦合性。将具体的new的操作放到子类工厂来实现。
    应用:
    说实话,找应用费了好久,毕竟整天Pizza,Pizza的很难给我们以启发,仅仅是知道模式,而没有看到它的巨大潜力和价值。
    学习MFC的都知道Document和Application的关系。不同的Application对应不同的Document。app负责创建doc。app就是工厂,doc就是产品,一个工厂一个产品。

    //App是工厂,它保存着创建的产品
    	//App
    	class Application
    	{
    	public:
    		void OpenDocument(){//模板方法
    			m_pDoc = CreateDocment();
    			m_pDoc->Open();
    			m_pDoc->Read();
    		}
    		void CloseDocument(){//模板方法
    			m_pDoc->Write();
    			m_pDoc->Close();
    		}
    	private:
    		virtual Document* CreateDocment() = 0;//工厂方法
    	private:
    		Document* m_pDoc;//这个
    	}
    	
    	//抽象产品类,文档可以打开关闭,读写。
    	class Document 
    	{
    	public:
    		void Open();
    		void Close();
    		void Read();
    		void Write();
    	}



    解释:
    上面两个类一看到就会有种欣喜的感觉,将模板方法和工厂方法完美的组合了起来。工厂方法关心的是创建对象,由具体的工厂MyApp负责创建实现具体的产品MyDocument.同时,模板方法OpenDocument面向抽象的创建接口编程,负责定义了一套算法,子类继承了Application,就继承了一套算法,子类不用关心算法执行的步骤,子类只需要实现方法,父类负责调用子类的方法。
    像经常看到的状态机代码,状态机的跳转流程是由预先定义的流程定义的,但是OnStateEnter和OnStateExit这样的方法的实现是由具体应用定义的。父类负责调用子类OnStateEnter和OnStateExit方法的具体的实现,模板方法的典型应用。

    抽象工厂:
    抽象工厂关注的是创建一系列的产品,它是使用工厂方法实现的,不过是拥有多个工厂方法。
    代码就不写了。
    总结(摘录的一段,感觉写的不错,地址 http://stan001140.iteye.com/blog/1737817):
    简单工厂能把具体实现包装起来,让客户端真正达到面向接口编程 
    工厂方法可以在高层进行编码,让服务端的产品线真正达到面向接口编程 
    抽象工厂能聚合整个产品簇,让整个服务端的多个产品线真正达到面向接口编程 
    模板方法同样是在高层进行编码,也同样是面向接口编程。 
    但工厂方法及抽象工厂方法着重抽象的是产品,而模板方法着重抽象的是步骤。 
    而我们通常会两者一起结合起来使用。

  • 相关阅读:
    从零搭建Spring Boot脚手架(4):手写Mybatis通用Mapper
    从零搭建Spring Boot脚手架(3):集成mybatis
    从零搭建Spring Boot脚手架(2):增加通用的功能
    从零搭建Spring Boot脚手架(1):开篇以及技术选型
    Hibernate Validator校验参数全攻略
    Spring Data R2DBC响应式操作MySQL
    Spring Security 实战干货:从零手写一个验证码登录
    Spring Security 实战干货:图解用户是如何登录的
    基于.NetCore3.1系列 —— 日志记录之日志核心要素揭秘
    基于.NetCore3.1系列 —— 日志记录之日志配置揭秘
  • 原文地址:https://www.cnblogs.com/james1207/p/3260681.html
Copyright © 2011-2022 走看看