zoukankan      html  css  js  c++  java
  • 初学设计模式【4】简单工厂模式——SimpleFactory

       在java中我们是通过new来实例化一个对象的,但这似乎和面向对象程序设计的一个原则(针对超类型编程,不针对具体实现编程)相悖。因为每当我们用new时就必然会针对具体类,为了解决这个问题就有了一个新的设计模式——工厂模式。 与别的模式不同工厂模式包括三种——简单工厂模式、工厂方法模式抽象工厂模式。此篇介绍简单工厂模式。

     简单工厂模式

      严格来说,简单工厂不算一个设计模式,反而比较像是一种编程习惯。姑且就把当一个模式来学习吧。简单工厂模式主要是用来避免直接在客户代码中实例化具体对象以达到提高抽象层次避免针对具体编程的目的。

    UML类图

    图1

    分析1>Client代表客户代码,就是我们需要一个具体产品的地方,如果不用工厂模式我们可能会直接在此处new一个具体产品出来,然而通过简单工厂模式我们把创建具体产品的职责委托给了简单工厂类,简单工厂类中一般会有一个static的方法专门用来处理创建对象的任务。Product是我们抽取出来的一个抽象类,由它派生出具体产品。这样做的好处也是为了针对接口编程。

        2>这看起来似乎只是将创建对象的代码从一个类中转移到了另一个类中,这么做有什么好处呢?

        a.重复利用。创建对象的代码可能以后需要在许多类中用到,就上图而言我们可能有多个client

        b.封装变化。创建对象的代码最有可能是以后要不断变化的,因为我们可能会增加新的具体产品类,因此我们有必要将这部分代码封装起来,避免影响其它不大会改变的代码。

        c.避免在客户代码中出现具体实例化过程。这样做也是为了增强应对未来变化的能力

    实例

      有一个PizzaStore类,此类用来接受用户的订单并负责加工pizza,有一个SimplePizzaFactory类专职用来创建pizze对象,还有一个抽象pizza类,几个具体pizza类。此例uml类图与定义中的uml类图对应,不再提供此例uml类图。关键代码如下:

    1>Pizza

    public abstract class Pizza {
    	String pizzaName;
    
    	public String getPizzaName() {
    		return pizzaName;
    	}
    
    	void prepare() {
    		System.out.println(pizzaName + ":preparing");
    	}
    
    	public void bake() {
    		System.out.println(pizzaName + ":baking");
    	}
    
    	public void cut() {
    		System.out.println(pizzaName + ":cutting");
    	}
    
    	public void box() {
    		System.out.println(pizzaName + ":boxxing");
    	}
    }
    

    2>PizzaStore

    public class PizzaStore {
    
    	public Pizza orderPizza(String pizzaType) {
    		Pizza pizza;
    		// 实例化代码可能在未来发生变化,所以我们交给工厂,避免实例化具体对象
    		pizza = SimplePizzaFactory.createPizza(pizzaType);
    		// 下面的代码是不大会变化的部分
    		pizza.prepare();
    		pizza.bake();
    		pizza.cut();
    		pizza.box();
    		return pizza;
    	}
    }

    3> SimplePizzaFactory

    public class SimplePizzaFactory {
    
    	public static Pizza createPizza(String pizzaType) {
    		Pizza pizza = null;
    		if (pizzaType.equals("PizzaA")) {
    			pizza = new ConcretePizzaA("APizza");
    		} else if (pizzaType.equals("PizzaA")) {
    			pizza = new ConcretePizzaB("BPizza");
    		} else {
    			pizza = new ConcretePizzaA(" Default Pizza");
    		}
    
    		return pizza;
    	}
    }

     4>具体pizza类

    public class ConcretePizzaA extends Pizza {
    	public ConcretePizzaA(String pizzaName) {
    		super.pizzaName = pizzaName;
    	}
    }
    
    
    public class ConcretePizzaB extends Pizza {
    	public ConcretePizzaB(String pizzaName) {
    		super.pizzaName = pizzaName;
    	}
    }

     5>测试类

    import org.junit.Test;
    
    public class TestFactory {
    
    	@Test
    	public void test() {
    		PizzaStore store = new PizzaStore();
    		store.orderPizza("PizzaA");
    	}
    
    }
    

      

     测试结果:

     总结:

      使用场景
        工厂类负责创建的对象比较少;
        客户只知道传入工厂类的参数,对于如何创建对象(逻辑)不关心;
        由于简单工厂很容易违反高内聚责任分配原则,因此一般只在很简单的情况下应用。
      优点
        工厂类是整个模式的关键.包含了必要的逻辑判断,根据外界给定的信息,决定究竟应该创建哪个具体类的对象.通过使用工厂类,外界可以从直接创建具体产品对象的尴尬局面摆脱出来,仅仅需要    负责“消费”对象就可以了。而不必管这些对象究竟如何创建及如何组织的.明确了各自的职责和权利,有利于整个软件体系结构的优化。
      缺点
        由于工厂类集中了所有实例的创建逻辑,违反了高内聚责任分配原则,将全部创建逻辑集中到了一个工厂类中;它所能创建的类只能是事先考虑到的,如果需要添加新的类,则就需要改变工厂类了。当系统中的具体产品类不断增多的时候,可能会出现要求工厂类根据不同条件创建不同实例的需求.这种对条件的判断和对具体产品类型的判断交错在一起,很难避免模块功能的蔓延,对系统的维护和扩展非常不利。

     参考

      《Head First Design Pattern》,百度百科。

  • 相关阅读:
    JAVA Rest High Level Client如何取聚合后的数据
    elasticsearch中TermQuery查不到数据问题
    项目中redis改brpop阻塞模式为订阅模式的实现(二)
    项目中redis改brpop阻塞模式为订阅模式的实现(一)
    《算法笔记》5. 前缀树、桶排序、排序算法总结
    《算法笔记》4. 堆与堆排序、比较器详解
    《算法笔记》3. 归并排序、随机快排整理
    《算法笔记》2. 链表、栈、队列、递归、哈希表、顺序表
    《算法笔记》1. 复杂度、排序、二分、异或
    深入理解Java线程状态转移
  • 原文地址:https://www.cnblogs.com/byghui/p/3067952.html
Copyright © 2011-2022 走看看