尚硅谷设计模式学习(四)工厂模式

从披萨店的案例引入工厂模式

披萨的种类很多(比如 GreekPizza、CheesePizza 等)

  • 披萨制作完成后需要进行cut(切片工作),box(包装工作)
  • 完成披萨店的功能

一、简单工厂模式

1、基本介绍

简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。定义了一个创建对象的工厂类,由这个工厂类来封装实例化对象的代码。

在软件开发中,当我们会用到大量的创建某种、某类或者某批对象时,就会使用到工厂模式。

属于创建型模式。

2、代码实现

设计方案:   定义一个可以实例化 Pizaa 对象的工厂类,封装创建对象的代码。

  

Pizza接口

public interface Pizza {

    /**
     * 披萨切片
     */
    void cut();

    /**
     * 披萨包装
     */
    void box();
}

 奶酪披萨

public class CheesePizza implements Pizza {

    public void cut() {
        System.out.println("奶酪披萨切片中");
    }

    public void box() {
        System.out.println("奶酪披萨打包中");
    }
}

希腊披萨

public class GreekPizza implements Pizza {

    public void cut() {
        System.out.println("希腊披萨切片中");
    }

    public void box() {
        System.out.println("希腊披萨打包中");
    }
}

创建披萨实例的工厂

public class PizzaFactory {

    public static Pizza getPizza(String type) {
        if(type.equalsIgnoreCase("CheesePizza")){
            return new CheesePizza();
        }else if(type.equalsIgnoreCase("GreekPizza")){
            return new GreekPizza();
        }else {
            return null;
        }
    }

}

测试

public class Client {
    public static void main(String[] args) {
        Pizza cheesePizza = PizzaFactory.getPizza("CheesePizza");
        cheesePizza.cut();
        cheesePizza.box();
        
        Pizza greekPizza = PizzaFactory.getPizza("GreekPizza");
        greekPizza.cut();
        greekPizza.box();
    }
}

 结果

奶酪披萨切片中
奶酪披萨打包中
希腊披萨切片中
希腊披萨打包中

二、工厂方法模式

看一个新的需求,客户在点披萨时,可以点不同口味的披萨,比如北京的奶酪 pizza、北京的胡椒 pizza 或者是伦敦的奶酪 pizza、伦敦的胡椒  pizza。 

思路 1

使用简单工厂模式,创建不同的简单工厂类,比如 BJPizzaSimpleFactory,LDPizzaSimpleFactory等等。从当前这个案例来说,也是可以的,但是考虑到项目的规模,以及软件的可维护性、可扩展性并不是特别好。

思路 2

使用工厂方法模式

1、基本介绍

工厂方法模式:定义一个创建对象的抽象方法,由子类工厂决定要实例化的类。工厂方法模式将对象的实例化推迟到子类。

2、代码实现

 披萨类Pizza

public interface Pizza {

    /**
     * 披萨切片
     */
    void cut();

    /**
     * 披萨包装
     */
    void box();
}

四种披萨

public class BJChessPizza implements Pizza{

    public void cut() {
        System.out.println("北京奶酪披萨切片中");
    }

    public void box() {
        System.out.println("北京奶酪披萨打包中");
    }
}
public class BJPepperPizza implements Pizza{

    public void cut() {
        System.out.println("北京胡椒披萨切片中");
    }

    public void box() {
        System.out.println("北京胡椒披萨打包中");
    }
}
public class LDChessPizza implements Pizza{

    public void cut() {
        System.out.println("伦敦奶酪披萨切片中");
    }

    public void box() {
        System.out.println("伦敦奶酪披萨打包中");
    }
}
public class LDPepperPizza implements Pizza{

    public void cut() {
        System.out.println("伦敦胡椒披萨切片中");
    }

    public void box() {
        System.out.println("伦敦胡椒披萨打包中");
    }
}

Pizza抽象工厂

public abstract class PizzaFactory {

    public abstract Pizza getPizza(String type);

}

北京披萨工厂,伦敦披萨工厂

public class BJPizzaFactory extends PizzaFactory {
    @Override
    public Pizza getPizza(String type) {
        if(type.equalsIgnoreCase("chess")){
           return new BJChessPizza();
        }else if(type.equalsIgnoreCase("pepper")){
           return new BJPepperPizza();
        }else {
            return null;
        }
    }
}
public class LDPizzaFactory extends PizzaFactory {
    @Override
    public Pizza getPizza(String type) {
        if(type.equals("chess")){
            return new LDChessPizza();
        }else if(type.equals("pepper")){
            return new LDPepperPizza();
        }else {
            return null;
        }
    }
}

测试

public class Client {
    public static void main(String[] args) {
        //北京披萨工厂
        BJPizzaFactory bjPizzaFactory = new BJPizzaFactory();

        Pizza bjChessPizza = bjPizzaFactory.getPizza("chess");
        bjChessPizza.cut();
        bjChessPizza.box();

        Pizza bjPepperPizza = bjPizzaFactory.getPizza("pepper");
        bjPepperPizza.cut();
        bjPepperPizza.box();

        //伦敦披萨工厂
        LDPizzaFactory ldPizzaFactory = new LDPizzaFactory();

        Pizza ldChessPizza = ldPizzaFactory.getPizza("chess");
        ldChessPizza.cut();
        ldChessPizza.box();

        Pizza ldPepperPizza = ldPizzaFactory.getPizza("pepper");
        ldPepperPizza.cut();
        ldPepperPizza.box();
    }
}

结果

北京奶酪披萨切片中
北京奶酪披萨打包中
北京胡椒披萨切片中
北京胡椒披萨打包中
伦敦奶酪披萨切片中
伦敦奶酪披萨打包中
伦敦胡椒披萨切片中
伦敦胡椒披萨打包中

三、抽象工厂模式

新需求:有的人喜欢吃肯德基的披萨,有的人喜欢吃麦当劳的披萨

1、基本介绍

抽象工厂模式:为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类

2、代码实现 

披萨接口

public interface Pizza {

    /**
     * 披萨切片
     */
    void cut();

    /**
     * 披萨包装
     */
    void box();
}

商店A披萨,商店B披萨接口

public interface ShopAPizza extends Pizza {
}
public interface ShopBPizza extends Pizza {
}

商店A披萨的实现类

public class ShopABJChessPizza implements ShopAPizza{

    public void cut() {
        System.out.println("商店A北京奶酪披萨切片中");
    }

    public void box() {
        System.out.println("商店A北京奶酪披萨打包中");
    }
}
public class ShopALDChessPizza implements ShopAPizza{

    public void cut() {
        System.out.println("商店A伦敦奶酪披萨切片中");
    }

    public void box() {
        System.out.println("商店A伦敦奶酪披萨打包中");
    }
}

商店B披萨的实现类 

public class ShopBBJChessPizza implements ShopBPizza{

    public void cut() {
        System.out.println("商店B北京胡椒披萨切片中");
    }

    public void box() {
        System.out.println("商店B北京胡椒披萨打包中");
    }

}
public class ShopBLDChessPizza implements ShopBPizza{

    public void cut() {
        System.out.println("商店B伦敦胡椒披萨切片中");
    }

    public void box() {
        System.out.println("商店B伦敦胡椒披萨打包中");
    }

}

披萨工厂抽象类

public abstract class PizzaFactory {

    public abstract Pizza getBJChessPizza(String type);

    public abstract Pizza getLDChessPizza(String type);
}

 商店A,B的工厂类

public class ShopAPizzaFactory extends PizzaFactory {

    @Override
    public Pizza getBJChessPizza(String type) {
        return new ShopABJChessPizza();
    }

    @Override
    public Pizza getLDChessPizza(String type) {
        return new ShopALDChessPizza();
    }
}
public class ShopBPizzaFactory extends PizzaFactory {
    @Override
    public Pizza getBJChessPizza(String type) {
        return new ShopBBJChessPizza();
    }

    @Override
    public Pizza getLDChessPizza(String type) {
        return new ShopBLDChessPizza();
    }
}

 四、JDK中的工厂模式源码案例

比如说日历类Calendar就用到了简单工厂模式

我们一般这样使用

public class Test {
    public static void main(String[] args) {
        Calendar cal = Calendar.getInstance();
        System.out.println("年:" + cal.get(Calendar.YEAR));
        System.out.println("月:" + (cal.get(Calendar.MONTH) + 1));
        System.out.println("日:" + cal.get(Calendar.DAY_OF_MONTH));
        System.out.println("时:" + cal.get(Calendar.HOUR_OF_DAY));
        System.out.println("分:" + cal.get(Calendar.MINUTE));
        System.out.println("秒:" + cal.get(Calendar.SECOND));
    }
}

具体源码:就用到了简单工厂模式 

    /**
     * Gets a calendar using the default time zone and locale. The
     * <code>Calendar</code> returned is based on the current time
     * in the default time zone with the default
     * {@link Locale.Category#FORMAT FORMAT} locale.
     *
     * @return a Calendar.
     */
    public static Calendar getInstance()
    {
        return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
    }
    private static Calendar createCalendar(TimeZone zone,
                                           Locale aLocale)
//根据 TimeZone zone, Locale aLocale创建对应的实例
    {
        CalendarProvider provider =
            LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
                                 .getCalendarProvider();
        if (provider != null) {
            try {
                return provider.getInstance(zone, aLocale);
            } catch (IllegalArgumentException iae) {
                // fall back to the default instantiation
            }
        }

        Calendar cal = null;

        if (aLocale.hasExtensions()) {
            String caltype = aLocale.getUnicodeLocaleType("ca");
            if (caltype != null) {
                switch (caltype) {
                case "buddhist":
                cal = new BuddhistCalendar(zone, aLocale);
                    break;
                case "japanese":
                    cal = new JapaneseImperialCalendar(zone, aLocale);
                    break;
                case "gregory":
                    cal = new GregorianCalendar(zone, aLocale);
                    break;
                }
            }
        }
        if (cal == null) {
            // If no known calendar type is explicitly specified,
            // perform the traditional way to create a Calendar:
            // create a BuddhistCalendar for th_TH locale,
            // a JapaneseImperialCalendar for ja_JP_JP locale, or
            // a GregorianCalendar for any other locales.
            // NOTE: The language, country and variant strings are interned.
            if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
                cal = new BuddhistCalendar(zone, aLocale);
            } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
                       && aLocale.getCountry() == "JP") {
                cal = new JapaneseImperialCalendar(zone, aLocale);
            } else {
                cal = new GregorianCalendar(zone, aLocale);
            }
        }
        return cal;
    }

五、工厂模式小结

1、工厂模式的意义

将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到和主项目的依赖关系的解耦。从而提高项目的扩展和维护性。

在软件开发中,当我们会用到大量的创建某种、某类或者某批对象时,就会使用到工厂模式

2、三种工厂模式   (简单工厂模式、工厂方法模式、抽象工厂模式)

个人觉得这个区别在于产品,如果产品单一,最合适用工厂模式,但是如果有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式。再通俗深化理解下:工厂模式针对的是一个产品等级结构 ,抽象工厂模式针对的是面向多个产品等级结构的。

再来看看工厂方法模式与抽象工厂模式对比:

工厂方法模式

抽象工厂模式

针对的是一个产品等级结构针对的是面向多个产品等级结构
一个抽象产品类多个抽象产品类
可以派生出多个具体产品类每个抽象产品类可以派生出多个具体产品类
一个抽象工厂类,可以派生出多个具体工厂类一个抽象工厂类,可以派生出多个具体工厂类
每个具体工厂类只能创建一个具体产品类的实例每个具体工厂类可以创建多个具体产品类的实例

参考文章:(1条消息) 抽象工厂模式-与-工厂方法模式区别_wyxhd2008的博客-CSDN博客 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小鲁蛋儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值