MyException - 我的异常网
当前位置:我的异常网» 设计模式 » java设计模式-工厂模式

java设计模式-工厂模式

www.MyException.Cn  网友分享于:2015-02-06  浏览:0次
java设计模式-------工厂模式

java设计模式--------工厂模式


  • 分类和定义

1)简单工厂模式(Simple Factory):又称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。在简单工厂模式中,可以根据自变量的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。

2)工厂方法模式(Factory Method):又称为多形性工厂;工厂方法模式定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method是一个类的实例化延迟到其子类。 在工厂方法模式中,核心的工厂类不再负责所有的产品的创建,而是将具体创建的工作交给子类去做。这个核心类则摇身一变,成为了一个抽象工厂角色,仅负责给出具体工厂子类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。

3)抽象工厂模式(Abstract Factory):又称为工具箱,产生产品族,但不利于产生新的产品; 抽象工厂模式提供一个创建一系列或相互依赖的对象的接口,而无需指定它们具体的类。

  • 实例

简单工厂模式

一个批萨店,出售不同的批萨,有水果批萨、奶酪批萨、蔬菜批萨等等 。每一个批萨的制作过程: 准备(prepare)、烘烤(bake)、切片(cut)、装箱(box)。批萨店需要为客户提供各种各样的批萨,当然,为客户提供批萨前得制作批萨

首先看批萨的种类 定义批萨父类

abstract public class Pizza {
    String name;
    String dough;
    String sauce;
    ArrayList<String> toppings = new ArrayList<String>();

    public String getName() {
        return name;
    }

    public void prepare() {
        System.out.println("Preparing " + name);
    }

    public void bake() {
        System.out.println("Baking " + name);
    }

    public void cut() {
        System.out.println("Cutting " + name);
    }

    public void box() {
        System.out.println("Boxing " + name);
    }

    public String toString() {
        // code to display pizza name and ingredients
        StringBuffer display = new StringBuffer();
        display.append("---- " + name + " ----\n");
        display.append(dough + "\n");
        display.append(sauce + "\n");
        for (int i = 0; i < toppings.size(); i++) {
            display.append(toppings.get(i) + "\n");
        }
        return display.toString();
    }
}

批萨的种类有一些,依次继承父类实现批萨,这里用一个例子代表

public class CheesePizza extends Pizza {
    public CheesePizza() {
        name = "Cheese Pizza";
        dough = "Regular Crust";
        sauce = "Marinara Pizza Sauce";
        toppings.add("Fresh Mozzarella");
        toppings.add("Parmesan");
    }
}

定义批萨店 为 (批萨店专职生产批萨,但是它自己不能主动提供批萨,需要操作员来调用)

public class PizzaStore {
    SimplePizzaFactory factory;

    public PizzaStore(SimplePizzaFactory factory) {
        this.factory = factory;
    }

    public Pizza orderPizza(String type) {
        Pizza pizza;

        pizza = factory.createPizza(type);

        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();

        return pizza;
    }
}

定义一个操作员,即简单工厂类,通过工厂类可以大量的生产批萨

public class SimplePizzaFactory {

    public Pizza createPizza(String type) {
        Pizza pizza = null;

        if (type.equals("cheese")) {
            pizza = new CheesePizza();
        } else if (type.equals("pepperoni")) {
            pizza = new PepperoniPizza();
        } else if (type.equals("clam")) {
            pizza = new ClamPizza();
        } else if (type.equals("veggie")) {
            pizza = new VeggiePizza();
        }
        return pizza;
    }
}

如下类图为简单工厂模式


如果我们想吃其他地方的批萨怎么办呢,注意批萨店中的制作方法是写死的

工厂模式实例

接着上面的例子,上面的简单工厂操作中,我们仅仅实现了一种类型的批萨店PizzaStore,但是,现实中,可能每一个地方都有批萨店,如北京。上海各有各的批萨店,那么此时就需要创建更多的批萨店了

在上面的简单工厂模式中,批萨店的批萨制作模式是固定的,如准备(prepare)、烘烤(bake)、切片(cut)、装箱(box),所以,制作批萨的代码相当于绑定在批萨店中了,而现实中不同地方的批萨店的做法是不相同的

这时候就需要重新设计批萨店的,将批萨店变成抽象的超类,用其他子类继承

public abstract class PizzaStore {

    abstract Pizza createPizza(String item);

    public Pizza orderPizza(String type) {
        Pizza pizza = createPizza(type);
        System.out.println("--- Making a " + pizza.getName() + " ---");
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
}

其他地方的批萨店继承超类:每一个批萨店都有自己的制作方法


public class NYPizzaStore extends PizzaStore {

    Pizza createPizza(String item) {
        if (item.equals("cheese")) {
            return new NYStyleCheesePizza();
        } else if (item.equals("veggie")) {
            return new NYStyleVeggiePizza();
        } else if (item.equals("clam")) {
            return new NYStyleClamPizza();
        } else if (item.equals("pepperoni")) {
            return new NYStylePepperoniPizza();
        } else return null;
    }
}
public class ChicagoPizzaStore extends PizzaStore {

    Pizza createPizza(String item) {
            if (item.equals("cheese")) {
                    return new ChicagoStyleCheesePizza();
            } else if (item.equals("veggie")) {
                    return new ChicagoStyleVeggiePizza();
            } else if (item.equals("clam")) {
                    return new ChicagoStyleClamPizza();
            } else if (item.equals("pepperoni")) {
                    return new ChicagoStylePepperoniPizza();
            } else return null;
    }
}

批萨店的关系图如下:


再看此时的批萨,因为已经提供了比较多种类的批萨,不同地方的批萨店的同一款批萨做法不一样


import java.util.ArrayList;

public abstract class Pizza {
    String name;
    String dough;
    String sauce;
    ArrayList<String> toppings = new ArrayList<String>();

    void prepare() {
        System.out.println("Preparing " + name);
        System.out.println("Tossing dough...");
        System.out.println("Adding sauce...");
        System.out.println("Adding toppings: ");
        for (int i = 0; i < toppings.size(); i++) {
            System.out.println("   " + toppings.get(i));
        }
    }

    void bake() {
        System.out.println("Bake for 25 minutes at 350");
    }

    void cut() {
        System.out.println("Cutting the pizza into diagonal slices");
    }

    void box() {
        System.out.println("Place pizza in official PizzaStore box");
    }

    public String getName() {
        return name;
    }

    public String toString() {
        StringBuffer display = new StringBuffer();
        display.append("---- " + name + " ----\n");
        display.append(dough + "\n");
        display.append(sauce + "\n");
        for (int i = 0; i < toppings.size(); i++) {
            display.append((String) toppings.get(i) + "\n");
        }
        return display.toString();
    }
}

看同一款奶酪批萨的不同做法


public class NYStyleCheesePizza extends Pizza {

    public NYStyleCheesePizza() { 
        name = "NY Style Sauce and Cheese Pizza";
        dough = "Thin Crust Dough";
        sauce = "Marinara Sauce";

        toppings.add("Grated Reggiano Cheese");
    }
}

public class ChicagoStyleCheesePizza extends Pizza {

    public ChicagoStyleCheesePizza() { 
        name = "Chicago Style Deep Dish Cheese Pizza";
        dough = "Extra Thick Crust Dough";
        sauce = "Plum Tomato Sauce";

        toppings.add("Shredded Mozzarella Cheese");
    }

    void cut() {
        System.out.println("Cutting the pizza into square slices");
    }
}

工厂模式如何实现调用不同批萨过程:

public class PizzaTestDrive {

    public static void main(String[] args) {
        PizzaStore nyStore = new NYPizzaStore(); //首先,定义一个批萨店


    Pizza pizza = nyStore.orderPizza("cheese");//然后,在调用店中生产的批萨,得到一个奶酪批萨
        System.out.println("Ethan ordered a " + pizza.getName() + "\n");

        PizzaStore chicagoStore = new ChicagoPizzaStore(); //再接着定义一个其他地方的批萨店
        pizza = chicagoStore.orderPizza("cheese"); //得到该店里的奶酪批萨
        System.out.println("Joel ordered a " + pizza.getName() + "\n");

    }
}

工厂模式中很明显的将工厂(不同的批萨店)和产品(不同的批萨)分开了

如下图:

工厂类 

产品类 

工厂方法模式定义了一个创建对象的接口,但有子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。

- 简单工厂模式和工厂模式区别

简单工厂把全部的事情,在一个地方都处理完了,然而工厂方法却是创建一个框架,让子类决定要如何实现,比方说,在工厂方法中,oreserPizza()方法提供了一般的框架,以便创建批萨,orderPizza()方法依赖于工厂方法创建具体类,并制造出实际的批萨。可通过继承PizzaStore类,决定实际制造出的批萨是什么。简单工厂的做法,可以将对象的创建封装起来。但是简单工厂不具备工厂方法的弹性,因为简单工厂不能变更正在创建的产品。

这正体现了 工厂模式的精髓:要依赖抽象,而不是具体类(依赖倒置原则

几个指导方针,防止在OO设计时违反依赖倒置原则

  • 1 -变量不可以持有具体类的引用
  • 2-不要让类派生自具体类
  • 3-不要覆盖基类中以实现的方法

但是规则是死的,并不是要一定完全遵循,可以尽量

抽象工厂模式

接着上面的例子,我们已经实现了能够制作出不同地方的批萨。虽然各地批萨是采用当地的制作方法,但是原材料的采用没有一定得标准,就是说,这样可能会导致不同的加盟店加入了劣质的原材料,从而影响口碑。

现在就是要采用统一的原材料供应,实现统一的原材料供应系统。

注意,北京的批萨店采用的蘑菇跟上海店采用的蘑菇可能不同。这样每一个地方的原材料品种都有可能不同。

北京 : 小蘑菇,黄奶酪,小洋葱
上海 :  大蘑菇,白奶酪,大洋葱
深圳:  金针菇, 甜奶酪,短洋葱

首先,我们定义一个批萨原材料接口

public interface PizzaIngredientFactory {

    public Dough createDough();  //蘑菇 
    public Sauce createSauce();  //洋葱
    public Cheese createCheese(); //奶酪
}

然后定义北京的原材料厂,实现接口:



public class BJPizzaIngredientFactory implements PizzaIngredientFactory {

    public Dough createDough() {
        return new ThinCrustDough();  //小蘑菇
    }

    public Sauce createSauce() {
        return new MarinaraSauce();   //小洋葱
    }

    public Cheese createCheese() { 
        return new ReggianoCheese();   // 黄奶酪
    } 

}

原材料一个例子

public interface Dough {
    public String toString();
}

public class ThinCrustDough implements Dough {
    public String toString() {
        return "Thin Crust Dough";
    }
}

同理可以实现其他地方的原材料厂,这里代码就不一一例举了

有了新的原材料就可以重新做正宗原材料的批萨了


public abstract class Pizza {
    String name;

    Dough dough;  //三种原材料 
    Sauce sauce;
    Cheese cheese;

    abstract void prepare(); // 做批萨前的准备  原材料  这个地方和前面不一样是因为原材料已经一样了,需要重新实现

    void bake() {
        System.out.println("Bake for 25 minutes at 350");
    }

    void cut() {
        System.out.println("Cutting the pizza into diagonal slices");
    }

    void box() {
        System.out.println("Place pizza in official PizzaStore box");
    }

    void setName(String name) {
        this.name = name;
    }

    String getName() {
        return name;
    }

    public String toString() {
        StringBuffer result = new StringBuffer();
        result.append("---- " + name + " ----\n");
        if (dough != null) {
            result.append(dough);
            result.append("\n");
        }
        if (sauce != null) {
            result.append(sauce);
            result.append("\n");
        }
        if (cheese != null) {
            result.append(cheese);
            result.append("\n");
        }
        return result.toString();
    }
}

批萨超类已经做好了,那么就可以实现奶酪批萨了

public class CheesePizza extends Pizza {
    PizzaIngredientFactory ingredientFactory;

    public CheesePizza(PizzaIngredientFactory ingredientFactory) {
        this.ingredientFactory = ingredientFactory;
    }

    void prepare() {
        System.out.println("Preparing " + name);
        dough = ingredientFactory.createDough();
        sauce = ingredientFactory.createSauce();
        cheese = ingredientFactory.createCheese();
    }
}

奶酪批萨做好之后,就可以制作具有北京黄奶酪原材料的北京奶酪批萨了,但是在这之前,我们还需要一个北京批萨店,由于这里采用了全新的北京原材料(所以和上面已经不同了),北京批萨店如下:

public class BJPizzaStore extends PizzaStore {

    protected Pizza createPizza(String item) {
        Pizza pizza = null;
        PizzaIngredientFactory ingredientFactory = 
            new BJPizzaIngredientFactory();

        if (item.equals("cheese")) {

            pizza = new CheesePizza(ingredientFactory);
            pizza.setName("BeiJIng Style Cheese Pizza");

        } 
        return pizza;
    }
}

有了这些之后就可以下单叫北京奶酪批萨了



public class PizzaTestDrive {

    public static void main(String[] args) {
        PizzaStore nyStore = new BJPizzaStore();

        Pizza pizza = nyStore.orderPizza("cheese"); //先调用父类 pizzastroe  order方法
        System.out.println("Ethan ordered a " + pizza + "\n");

    }
}

类图如下所示:


可能很多人觉得工厂模式和抽象工厂模式很相似,这其实是有依据的。如上面,抽象工厂模式中有很多工厂模式的方法,如createDough()、createSauce()等等都是声明为抽象,然后用子类来实现。这个地方我们定义了一组对象产品,如 蘑菇、奶酪、洋葱等等都是一个个抽象的对象,都放在一起封装成一个 原材料 工厂 。
洋葱和奶酪应该是两个不相关的东西,但是通过抽象工厂模式将其组合到一起。
工厂模式中的pizzastroe提供一个接口,创建一个产品
抽象工厂模式中的 原材料 工厂 提供一个接口,创建 一个产品家族

三种模式的优缺点介绍

-简单工厂模式

  • 简单工厂模式的优点如下:
    (1)工厂类含有必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的责任,而仅仅“消费”产品;简单工厂模式通过这种做法实现了对责任的分割,它提供了专门的工厂类用于创建对象。
    (2)客户端无需知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以减少使用者的记忆量。
    (3)通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
  • 简单工厂模式的缺点如下:
    (1)由于工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。
    (2)使用简单工厂模式将会增加系统中类的个数,在一定程序上增加了系统的复杂度和理解难度。
    (3)系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。
    (3)简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。

- 工厂模式

  • 工厂方法模式的优点如下:
    (1)在工厂方法模式中,工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节,用户只需要关心所需产品对应的工厂,无需关心创建细节,甚至无需知道具体产品类的类名。
    (2)基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。它能够使工厂可以自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部。工厂方法模式之所以又被称为多态工厂模式,正是因为所有的具体工厂类都具有同一抽象父类。
    (3)使用工厂方法模式的另一个优点是在系统中加入新产品时,无需修改抽象工厂和抽象产品提供的接口,无需修改客户端,也无需修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以了,这样,系统的可扩展性也就变得非常好,完全符合“开闭原则”。
  • 工厂方法模式的缺点如下:
    (1)在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。
    (2)由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。

- 抽象工厂模式

  • 优点:
    (1) 隔离了具体类的生成,使得用户不需要知道什么被创建了。
    (2) 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。
  • 缺点:
    (1)添加新的产品对像时,难以扩展抽象工厂以便生产新种类的产品。

文章评论

写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
Web开发者需具备的8个好习惯
Web开发者需具备的8个好习惯
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
程序员的鄙视链
程序员的鄙视链
编程语言是女人
编程语言是女人
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
Java 与 .NET 的平台发展之争
Java 与 .NET 的平台发展之争
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
要嫁就嫁程序猿—钱多话少死的早
要嫁就嫁程序猿—钱多话少死的早
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
如何成为一名黑客
如何成为一名黑客
代码女神横空出世
代码女神横空出世
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
2013年美国开发者薪资调查报告
2013年美国开发者薪资调查报告
不懂技术不要对懂技术的人说这很容易实现
不懂技术不要对懂技术的人说这很容易实现
10个调试和排错的小建议
10个调试和排错的小建议
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
那些争议最大的编程观点
那些争议最大的编程观点
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
 程序员的样子
程序员的样子
漫画:程序员的工作
漫画:程序员的工作
程序员都该阅读的书
程序员都该阅读的书
Java程序员必看电影
Java程序员必看电影
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
Google伦敦新总部 犹如星级庄园
Google伦敦新总部 犹如星级庄园
旅行,写作,编程
旅行,写作,编程
总结2014中国互联网十大段子
总结2014中国互联网十大段子
中美印日四国程序员比较
中美印日四国程序员比较
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
程序员必看的十大电影
程序员必看的十大电影
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
老程序员的下场
老程序员的下场
每天工作4小时的程序员
每天工作4小时的程序员
我的丈夫是个程序员
我的丈夫是个程序员
我是如何打败拖延症的
我是如何打败拖延症的
做程序猿的老婆应该注意的一些事情
做程序猿的老婆应该注意的一些事情
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有