工厂模式

Posted RB2010

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了工厂模式相关的知识,希望对你有一定的参考价值。

工厂模式

需求:设计一个咖啡店点餐系统。

设计一个咖啡类(Coffee),并定义其两个子类(美式咖啡【AmericanCoffee】和拿铁咖啡【LatteCoffee】);再设计一个咖啡店类(CoffeeStore),咖啡店具有点咖啡的功能。

具体类的设计如下:

在java中,万物皆对象,这些对象都需要创建,如果创建的时候直接new该对象,就会对该对象耦合严重,假如我们要更换对象,所有new对象的地方都需要修改一遍,这显然违背了软件设计的开闭原则。如果我们使用工厂来生产对象,我们就只和工厂打交道就可以了,彻底和对象解耦,如果要更换对象,直接在工厂里更换该对象即可,达到了与对象解耦的目的;所以说,工厂模式最大的优点就是:解耦

在本教程中会介绍三种工厂的使用

  • 简单工厂模式(不属于GOF的23种经典设计模式)
  • 工厂方法模式
  • 抽象工厂模式

简单工厂模式

简单工厂不是一种设计模式,反而比较像是一种编程习惯。

结构

简单工厂包含如下角色:

  • 抽象产品 :定义了产品的规范,描述了产品的主要特性和功能。
  • 具体产品 :实现或者继承抽象产品的子类
  • 具体工厂 :提供了创建产品的方法,调用者通过该方法来获取产品。

实现

现在使用简单工厂对上面案例进行改进,类图如下:

工厂类代码如下:

public class SimpleCoffeeFactory 

    public Coffee createCoffee(String type) 
        Coffee coffee = null;
        if("americano".equals(type)) 
            coffee = new AmericanoCoffee();
         else if("latte".equals(type)) 
            coffee = new LatteCoffee();
        
        return coffee;
    

工厂(factory)处理创建对象的细节,一旦有了SimpleCoffeeFactory,CoffeeStore类中的orderCoffee()就变成此对象的客户,后期如果需要Coffee对象直接从工厂中获取即可。这样也就解除了和Coffee实现类的耦合,同时又产生了新的耦合,CoffeeStore对象和SimpleCoffeeFactory工厂对象的耦合,工厂对象和商品对象的耦合。

后期如果再加新品种的咖啡,我们势必要需求修改SimpleCoffeeFactory的代码,违反了开闭原则。工厂类的客户端可能有很多,比如创建美团外卖等,这样只需要修改工厂类的代码,省去其他的修改操作。

扩展

  • 静态工厂

在开发中也有一部分人将工厂类中的创建对象的功能定义为静态的,这个就是静态工厂模式,它也不是23种设计模式中的。代码如下:

public class SimpleCoffeeFactory 

    public static Coffee createCoffee(String type) 
        Coffee coffee = null;
        if("americano".equals(type)) 
            coffee = new AmericanoCoffee();
         else if("latte".equals(type)) 
            coffee = new LatteCoffee();
        
        return coffe;
    

优缺点

  • 优点:

封装了创建对象的过程,可以通过参数直接获取对象。把对象的创建和业务逻辑层分开,这样以后就避免了修改客户代码,如果要实现新产品直接修改工厂类,而不需要在原代码中修改,这样就降低了客户代码修改的可能性,更加容易扩展。

  • 缺点:

增加新产品时还是需要修改工厂类的代码,违背了“开闭原则”。

工厂方法模式

针对上例中的缺点,使用工厂方法模式就可以完美的解决,完全遵循开闭原则。

概念

定义一个用于创建对象的接口,让子类决定实例化哪个产品类对象。工厂方法使一个产品类的实例化延迟到其工厂的子类。

结构

工厂方法模式的主要角色:

  • 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品。
  • 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
  • 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
  • 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。

实现

使用工厂方法模式对上例进行改进,类图如下:

代码如下:

抽象工厂:

public interface CoffeeFactory 

    Coffee createCoffee();

具体工厂:

public class LatteCoffeeFactory implements CoffeeFactory 

    public Coffee createCoffee() 
        return new LatteCoffee();
    


public class AmericanCoffeeFactory implements CoffeeFactory 

    public Coffee createCoffee() 
        return new AmericanCoffee();
    

咖啡店类:

public class CoffeeStore 

    private CoffeeFactory factory;

    public CoffeeStore(CoffeeFactory factory) 
        this.factory = factory;
    

    public Coffee orderCoffee(String type) 
        Coffee coffee = factory.createCoffee();
        coffee.addMilk();
        coffee.addsugar();
        return coffee;
    

从以上的编写的代码可以看到,要增加产品类时也要相应地增加工厂类,不需要修改工厂类的代码了,这样就解决了简单工厂模式的缺点。

工厂方法模式是简单工厂模式的进一步抽象。由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。

优缺点

  • 优点:

    • 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程;
    • 在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则;
  • 缺点:

    • 每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度。

抽象工厂模式

前面介绍的工厂方法模式中考虑的是一类产品的生产,如畜牧场只养动物、电视机厂只生产电视机、传智播客只培养计算机软件专业的学生等。

这些工厂只生产同种类产品,同种类产品称为同等级产品,也就是说:工厂方法模式只考虑生产同等级的产品,但是在现实生活中许多工厂是综合型的工厂,能生产多等级(种类) 的产品,如电器厂既生产电视机又生产洗衣机或空调,大学既有软件专业又有生物专业等。

本节要介绍的抽象工厂模式将考虑多等级产品的生产,将同一个具体工厂所生产的位于不同等级的一组产品称为一个产品族,下图所示横轴是产品等级,也就是同一类产品;纵轴是产品族,也就是同一品牌的产品,同一品牌的产品产自同一个工厂。

概念

是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。

抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品。

结构

抽象工厂模式的主要角色如下:

  • 抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法,可以创建多个不同等级的产品。
  • 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
  • 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
  • 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它 同具体工厂之间是多对一的关系。

实现

现咖啡店业务发生改变,不仅要生产咖啡还要生产甜点,如提拉米苏、抹茶慕斯等,要是按照工厂方法模式,需要定义提拉米苏类、抹茶慕斯类、提拉米苏工厂、抹茶慕斯工厂、甜点工厂类,很容易发生类爆炸情况。其中拿铁咖啡、美式咖啡是一个产品等级,都是咖啡;提拉米苏、抹茶慕斯也是一个产品等级;拿铁咖啡和提拉米苏是同一产品族(也就是都属于意大利风味),美式咖啡和抹茶慕斯是同一产品族(也就是都属于美式风味)。所以这个案例可以使用抽象工厂模式实现。类图如下:

代码如下:

抽象工厂:

public interface DessertFactory 

    Coffee createCoffee();

    Dessert createDessert();

具体工厂:

//美式甜点工厂
public class AmericanDessertFactory implements DessertFactory 

    public Coffee createCoffee() 
        return new AmericanCoffee();
    

    public Dessert createDessert() 
        return new MatchaMousse();
    

//意大利风味甜点工厂
public class ItalyDessertFactory implements DessertFactory 

    public Coffee createCoffee() 
        return new LatteCoffee();
    

    public Dessert createDessert() 
        return new Tiramisu();
    

如果要加同一个产品族的话,只需要再加一个对应的工厂类即可,不需要修改其他的类。

优缺点

  • 优点:

    • 当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
  • 缺点:

    • 当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。

使用场景

  • 当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机、空调等。

  • 系统中有多个产品族,但每次只使用其中的某一族产品。如有人只喜欢穿某一个品牌的衣服和鞋。

  • 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。

如:输入法换皮肤,一整套一起换。生成不同操作系统的程序。

模式扩展

简单工厂+配置文件解除耦合

可以通过工厂模式+配置文件的方式解除工厂对象和产品对象的耦合。在工厂类中加载配置文件中的全类名,并创建对象进行存储,客户端如果需要对象,直接进行获取即可。

第一步:定义配置文件

为了演示方便,我们使用properties文件作为配置文件,名称为bean.properties

american=com.itheima.pattern.factory.config_factory.AmericanCoffee
latte=com.itheima.pattern.factory.config_factory.LatteCoffee

第二步:改进工厂类

public class CoffeeFactory 

    private static Map<String,Coffee> map = new HashMap();

    static 
        Properties p = new Properties();
        InputStream is = CoffeeFactory.class.getClassLoader().getResourceAsStream("bean.properties");
        try 
            p.load(is);
            //遍历Properties集合对象
            Set<Object> keys = p.keySet();
            for (Object key : keys) 
                //根据键获取值(全类名)
                String className = p.getProperty((String) key);
                //获取字节码对象
                Class clazz = Class.forName(className);
                Coffee obj = (Coffee) clazz.newInstance();
                map.put((String)key,obj);
            
         catch (Exception e) 
            e.printStackTrace();
        
    

    public static Coffee createCoffee(String name) 

        return map.get(name);
    

静态成员变量用来存储创建的对象(键存储的是名称,值存储的是对应的对象),而读取配置文件以及创建对象写在静态代码块中,目的就是只需要执行一次。

JDK源码解析-Collection.iterator方法

public class Demo 
    public static void main(String[] args) 
        List<String> list = new ArrayList<>();
        list.add("令狐冲");
        list.add("风清扬");
        list.add("任我行");

        //获取迭代器对象
        Iterator<String> it = list.iterator();
        //使用迭代器遍历
        while(it.hasNext()) 
            String ele = it.next();
            System.out.println(ele);
        
    

对上面的代码大家应该很熟,使用迭代器遍历集合,获取集合中的元素。而单列集合获取迭代器的方法就使用到了工厂方法模式。我们看通过类图看看结构:

Collection接口是抽象工厂类,ArrayList是具体的工厂类;Iterator接口是抽象商品类,ArrayList类中的Iter内部类是具体的商品类。在具体的工厂类中iterator()方法创建具体的商品类的对象。

另:

​ 1,DateForamt类中的getInstance()方法使用的是工厂模式;

​ 2,Calendar类中的getInstance()方法使用的是工厂模式;

PS:本章内容转自黑马程序员,写的非常通俗易懂,直接转来,感谢

C++实现工厂模式(简单工厂模式工厂方法模式抽象工厂模式)

设计模式中的工厂模式,在项目中使用还是很频繁,工厂模式细分为三种:简单工厂模式、工厂方法模式、抽象工厂模式。
## 简单工厂模式
class Widget

public:
	typedef enum
	
		WidgetA = 0,
		WidgetB
	Type;
	explicit Widget(Type type) :m_type(type)
	
		
	

	virtual ~Widget()
	

	
protected:
	Type m_type;
;

class WidgteA : public Widget 

public:
	explicit WidgteA(Type type) :Widget(type)
	
		std::cout << "创建A窗口" << std::endl;
	

;

class WidgteB : public Widget

public:
	explicit WidgteB(Type type) :Widget(type)
	
		std::cout << "创建B窗口" << std::endl;
	

;

class FactoryWidget

public:
	explicit FactoryWidget()

	Widget* WinFactory(Widget::Type type)
	
		switch (type)
		
		case Widget::WidgetA:
			return new WidgteA(type);
		case Widget::WidgetB:
			return new WidgteB(type);
		default:
			break;
		

		return nullptr;
	
;
简单工厂模式的优缺点:
优点:简单,容易理解,但是违背设计原则中的开闭原则
缺点:就上面的例子,如果增加一个新的窗口,就要修改原先的代码很不方便
## 工厂方法模式
class Actor

public:
	explicit Actor(int type, std::string name) 
		: m_iType(type)
		, m_strName(name)
	

	

	virtual ~Actor()
	

	

private:
	int m_iType;

	std::string m_strName;
;

class ActorA : public Actor

public:
	explicit ActorA(int type, std::string name)
		: Actor(type, name)
	
		std::cout << "角色创建:"<< name << std::endl;
	
	virtual ~ActorA()
	

	
;

class ActorB : public Actor

public:
	explicit ActorB(int type, std::string name)
		: Actor(type, name)
	
		std::cout << "角色创建:"<<name << std::endl;
	
	virtual ~ActorB()
	

	
;
//
class Factory

public:
	explicit Factory(int type, const std::string name)
		: m_iType(type)
		, m_strName(name)
	
		
	
	virtual ~Factory()
	

	

	virtual Actor* creatFactory()
	
		return new Actor(m_iType, m_strName);
	
protected:
	int m_iType;
	std::string m_strName;
;

class FactoryA : public Factory


public:
	explicit FactoryA(int type, const std::string name)
		: Factory(type, name)
	
		std::cout << "工厂:" << name << std::endl;
	

	Actor* creatActor()
	
		return new ActorA(m_iType, m_strName);
	
;

class FactoryB : public Factory


public:
	explicit FactoryB(int type, const std::string name)
		: Factory(type, name)
	
		std::cout << "工厂:" << name << std::endl;
	

	Actor* creatActor()
	
		return new ActorB(m_iType, m_strName);
	
;


template<typename T>
class ActorFactory

public:
	explicit ActorFactory()
	

	
	virtual ~ActorFactory()
	

	
	T* creatorFactory(const int type, const std::string name)
	
		return new T(type, name);
	
;
工厂方法模式的优缺点:
优点:上面的例子如果增加新的角色,直接派生基类增加,和增加新的工厂即可,不影响原来的代码,符合开闭原则
缺点:一个工厂只能进行一个角色的诞生,有多少角色就有多少工厂,
## 抽象工厂模式
class Body

public:
	explicit Body(std::string name) :m_strName(name)
	

	
	virtual ~Body() 

	virtual std::string getBodyName() = 0;
protected:
	std::string m_strName;
;

class Clothes

public:
	explicit Clothes(std::string name) :m_strName(name)
	

	
	virtual ~Clothes() 
	virtual std::string getClothesName() = 0;

protected:
	std::string m_strName;
;

class Shoe

public:
	explicit Shoe(std::string name)
		: m_strName(name)
	

	
	virtual ~Shoe() 
	virtual std::string getShoeName() = 0;

protected:
	std::string m_strName;
;

class DollBody : public Body

public:
	explicit DollBody(std::string name)
		: Body(name)
	

	

	std::string getBodyName()
	
		return m_strName;
	

private:

;

class DollClothes : public Clothes

public:
	explicit DollClothes(std::string name) :Clothes(name)
	

	

	std::string getClothesName()
	
		return m_strName;
	

private:

;


class DollShoe : public Shoe

public:
	explicit DollShoe(std::string name) : Shoe(name)
	

	

	std::string getShoeName()
	
		return m_strName;
	

private:

;
class Doll

public:
	explicit Doll( Body* pBody,  Clothes* pClothes,  Shoe* pShoe)
		: m_pBody(pBody)
		, m_pClothes(pClothes)
		, m_pShoe(pShoe)
	

	
	void Assemble()
	
		std::cout << m_pBody->getBodyName() << std::endl;;
		std::cout << m_pClothes->getClothesName() << std::endl;
		std::cout << m_pShoe->getShoeName()<<std::endl;
	

private:
	Body* m_pBody;
	Clothes* m_pClothes;
	Shoe* m_pShoe;
;

class FactoryAbs

public:
	virtual ~FactoryAbs() 

	virtual Body* creatBody() = 0;
	virtual Clothes* creatClothes() = 0;
	virtual Shoe* creatShoe() = 0;
	virtual std::string name() = 0;
protected:
	std::string m_strName;
;

class ZheJiangFactory : public FactoryAbs

public:
	explicit ZheJiangFactory(std::string name)
		: m_strName(name)
	

	
	virtual ~ZheJiangFactory()
	

	

	Body* creatBody()
	
		return new DollBody("浙江生产玩具身体");
	

	Clothes* creatClothes()
	
		return new DollClothes("浙江生产玩具衣服");
	

	Shoe* creatShoe()
	
		return new DollShoe("浙江生产玩具鞋");
	

	std::string name()
	
		return m_strName;
	

private:
	std::string m_strName;
;

class JiangSuFactory : public FactoryAbs

public:
	explicit JiangSuFactory(std::string name)
		: m_strName(name)
	

	
	virtual ~JiangSuFactory()
	

	
	 Body* creatBody()
	
		return new DollBody("江苏生产玩具身体");
	

	 Clothes* creatClothes()
	
		return new DollClothes("江苏生产玩具衣服");
	

	 Shoe* creatShoe()
	
		return new DollShoe("江苏生产玩具鞋");
	

	 std::string name()
	
		return m_strName;
	

private:
	std::string m_strName;
;
抽象工厂模式的优缺点:
优点:代码扩展灵活,符合开闭原则
缺点:代码量大,复杂

方法调用
int main()

	//工厂方法模式
	Actor* pB = (new ActorFactory<FactoryB>())->creatorFactory(0, "B")->creatActor();
	Actor* pA = (new ActorFactory<FactoryA>())->creatorFactory(0, "A")->creatActor();

	//简单工厂模式
	FactoryWidget* pFwin = new FactoryWidget();
	Widget* pWinA = pFwin->WinFactory(Widget::WidgetA);
	Widget* pWinB = pFwin->WinFactory(Widget::WidgetB);

	//抽象工厂模式
	FactoryAbs* pZheJiangFactory = new ZheJiangFactory("浙江工厂");
	Body* pBody = pZheJiangFactory->creatBody();
	Clothes* pClothes = pZheJiangFactory->creatClothes();
	Shoe* pShoe = pZheJiangFactory->creatShoe();
	Doll* pDoll = new Doll(pBody, pClothes, pShoe);
	pDoll->Assemble();

	FactoryAbs* pJiangSuFactory = new JiangSuFactory("江苏工厂");
	Body* pBody1 = pJiangSuFactory->creatBody();
	Clothes* pClothes1 = pJiangSuFactory->creatClothes();
	Shoe* pShoe1 = pJiangSuFactory->creatShoe();
	Doll* pDoll1 = new Doll(pBody1, pClothes1, pShoe1);
	pDoll1->Assemble();

	//多个工厂配合生产一个产品
	FactoryAbs* pZheJiangFactory1 = new ZheJiangFactory("浙江工厂");
	Body* pZJBody = pZheJiangFactory1->creatBody();

	FactoryAbs* pJiangSuFactory1 = new JiangSuFactory("江苏工厂");
	Clothes* pJSClothes1 = pJiangSuFactory1->creatClothes();
	Shoe* pJSShoe1 = pJiangSuFactory1->creatShoe();
	Doll* pM = new Doll(pZJBody, pJSClothes1, pJSShoe1);
	pM->Assemble();
	return 0;


以上就是工厂模式中的三种模式,各有优缺点,如果不牵扯到代码的扩展,用简单的工厂模式,简单方便,容易理解,如果有代码的扩展需求,工厂方法模式比较合适,但是,需要定义很多工厂,工厂代码太多,如果牵扯一个工厂有多个产品线,那只能使用抽象工厂模式,代码量大,比较复杂,所以在项目开发过程中,选择适合自己的设计模式是最重要的,灵活变通。

以上是关于工厂模式的主要内容,如果未能解决你的问题,请参考以下文章

工厂模式抽象工厂模式策略模式

JAVA设计模式——工厂模式简单工厂模式工厂方法模式抽象工厂模式

C++实现工厂模式(简单工厂模式工厂方法模式抽象工厂模式)

设计模式之工厂模式详解和应用

iOS经常使用设计模式——工厂方法(简单工厂模式,工厂方法模式, 抽象工厂模式)

设计模式——抽象工厂模式