设计模式七--工厂模式

161 阅读9分钟

这是我参与更文挑战的第20天,活动详情查看: 更文挑战

工厂模式

简单工厂模式

比如我们有一个饭店,饭店的厨师做菜有以下几步1、选择食材 2、切菜 3、炒菜4、装盘 5、上菜

此时2、3、4、5无论什么菜都是一样的,我们这样写

Cook

package com.wangscaler.factory;

/**
 * @author wangscaler
 * @date 2021.06.22 16:17
 */
public abstract class Cook {
    protected String name;

    public abstract void prepare();

    public void cut() {
        System.out.println(name + "的材料被切好了");
    }

    public void fry() {
        System.out.println(name + "被炒好了");
    }

    public void dish() {
        System.out.println(name + "装进盘子里了");
    }

    public void serve() {
        System.out.println(name + "端上饭桌了");
    }

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

FishFlavoredPork

package com.wangscaler.factory;
/**
 * @author wangscaler
 * @date 2021.06.22 16:17
 */
public class FishFlavoredPork extends Cook {
    @Override
    public void prepare() {
        System.out.println("胡萝卜,肉丝,甜面酱等材料准备好了");
    }
}

KungPaoChicken

package com.wangscaler.factory;
/**
 * @author wangscaler
 * @date 2021.06.22 16:17
 */
public class KungPaoChicken extends Cook {
    @Override
    public void prepare() {
        System.out.println("鸡丁,黄瓜,花生等材料准备好了");
    }
}

Order

package com.wangscaler.factory;

import java.io.BufferedReader;
import java.io.InputStreamReader;

/**
 * @author wangscaler
 * @date 2021.06.22 16:17
 */
public class Order {
    public Order() {
        Cook cook = null;
        String type;
        do {
            type = geType();
            if (type.equals(Menu.FISHFLAVOREDPORK.getName())) {
                cook = new FishFlavoredPork();
                cook.setName("鱼香肉丝");
            } else if (type.equals(Menu.KUNGPAOCHICKEN.getName())) {
                cook = new KungPaoChicken();
                cook.setName("宫保鸡丁");
            } else if (type.equals(Menu.EOF.getName())) {
                break;
            } else {
                break;
            }
            cook.prepare();
            cook.cut();
            cook.fry();
            cook.dish();
            cook.serve();
        } while (true);
    }

    public enum Menu {
        /**
         * 鱼香肉丝
         */
        FISHFLAVOREDPORK("fishflavoredpork"),
        /**
         * 宫保鸡丁
         */
        KUNGPAOCHICKEN("kungpaochicken"),
        /**
         * 结束标识
         */
        EOF("EOF");
        private String name;

        Menu(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

    }

    private String geType() {
        try {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("请输入您要点的菜,以EOF为结束");
            String str = bufferedReader.readLine();
            return str;
        } catch (Exception e) {
            e.printStackTrace();
            return "";

        }
    }
}

main

package com.wangscaler.factory;
/**
 * @author wangscaler
 * @date 2021.06.22 16:17
 */
public class SimpleFactory {
    public static void main(String[] args) {
     Order order =new Order();
    }

}

此时运行结果,一切正常

请输入您要点的菜,以EOF为结束
kungpaochicken
鸡丁,黄瓜,花生等材料准备好了
宫保鸡丁的材料被切好了
宫保鸡丁被炒好了
宫保鸡丁装进盘子里了
宫保鸡丁端上饭桌了
请输入您要点的菜,以EOF为结束
fishflavoredpork
胡萝卜,肉丝,甜面酱等材料准备好了
鱼香肉丝的材料被切好了
鱼香肉丝被炒好了
鱼香肉丝装进盘子里了
鱼香肉丝端上饭桌了
请输入您要点的菜,以EOF为结束
EOF

然而,当我们在菜单增加新的菜谱时,不仅要增加新的类,还要修改Order这个类,如果有很多的饭店,我们可能要写很多Order这样改起来不仅麻烦,还违反了我们设计原则中开闭原则的对扩展开放、对修改关闭。

修改Order

package com.wangscaler.factory;

import java.io.BufferedReader;
import java.io.InputStreamReader;

/**
 * @author wangscaler
 * @date 2021.06.22 16:17
 */
public class Order {
    //    public Order() {
//        Cook cook = null;
//        String type;
//        do {
//            type = geType();
//            if (type.equals(Menu.FISHFLAVOREDPORK.getName())) {
//                cook = new FishFlavoredPork();
//                cook.setName("鱼香肉丝");
//            } else if (type.equals(Menu.KUNGPAOCHICKEN.getName())) {
//                cook = new KungPaoChicken();
//                cook.setName("宫保鸡丁");
//            } else if (type.equals(Menu.EOF.getName())) {
//                break;
//            } else {
//                break;
//            }
//            cook.prepare();
//            cook.cut();
//            cook.fry();
//            cook.dish();
//            cook.serve();
//        } while (true);
//    }
    SimpleFactory simpleFactory;
    Cook cook = null;

    public Order(SimpleFactory simpleFactory) {
        setFactory(simpleFactory);
    }

    public void setFactory(SimpleFactory simpleFactory) {
        String type = "";
        this.simpleFactory = simpleFactory;
        do {
            type = geType();
            cook = this.simpleFactory.createCook(type);
            if (cook != null) {
                cook.prepare();
                cook.cut();
                cook.fry();
                cook.dish();
                cook.serve();
            } else {
                break;
            }
        } while (true);
    }

    private String geType() {
        try {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("请输入您要点的菜,以EOF为结束");
            String str = bufferedReader.readLine();
            return str;
        } catch (Exception e) {
            e.printStackTrace();
            return "";

        }
    }
}

修改SimpleFactory

package com.wangscaler.factory;

/**
 * @author wangscaler
 * @date 2021.06.22 16:17
 */
public class SimpleFactory {
    public Cook createCook(String type) {
        Cook cook = null;
        if (type.equals(SimpleFactory.Menu.FISHFLAVOREDPORK.getName())) {
            cook = new FishFlavoredPork();
            cook.setName("鱼香肉丝");
        } else if (type.equals(SimpleFactory.Menu.KUNGPAOCHICKEN.getName())) {
            cook = new KungPaoChicken();
            cook.setName("宫保鸡丁");
        }
        return cook;
    }
  public enum Menu {
        /**
         * 鱼香肉丝
         */
        FISHFLAVOREDPORK("fishflavoredpork"),
        /**
         * 宫保鸡丁
         */
        KUNGPAOCHICKEN("kungpaochicken"),
        /**
         * 结束标识
         */
        EOF("EOF");
        private String name;

        Menu(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

    }
    public static void main(String[] args) {
        new Order(new SimpleFactory());
    }

}

当我们新增加菜单时,只需要增加新的菜谱的类,和修改工厂,对Order 没有影响。简单工厂模式也叫静态工厂模式,是因为我们的代码可以修改成

SimpleFactory的createCook方法加上static,之后 修改Order

package com.wangscaler.factory;

import java.io.BufferedReader;
import java.io.InputStreamReader;

/**
 * @author wangscaler
 * @date 2021.06.22 16:17
 */
public class Order1 {
    Cook cook = null;
    String type = "";

    public Order1() {
        do {
            type = geType();
            cook = SimpleFactory.createCook(type);
            if (cook != null) {
                cook.prepare();
                cook.cut();
                cook.fry();
                cook.dish();
                cook.serve();
            } else {
                break;
            }
        } while (true);
    }

    private String geType() {
        try {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("请输入您要点的菜,以EOF为结束");
            String str = bufferedReader.readLine();
            return str;
        } catch (Exception e) {
            e.printStackTrace();
            return "";

        }
    }
}

由于所有的菜都是SimpleFactory来生产的,如果这个工厂出现问题,那么他生产出来的所有的菜也就跟着出了问题。而下面的这个模式,将对象的实例化延迟到子类。

工厂方法模式

比如我们现在不仅是有菜,还增加了味道,比如辣味、不辣三种味道的菜(能明白意思就行,不要计较能吃不能吃,哈哈),即辣味的鱼香肉丝和不辣的鱼香肉丝。

上述的Cook不修改,将鱼香肉丝变成辣味的(SpicyFishFlavoredPork)和不辣的(NotSpicyFishFlavoredPork),内容和之前的一样。然后我们修改了Order和增加两个工厂辣味工厂(SpicyFactory)和不辣工厂(NotSpicyFactory)。代码如下

factory.png

Order

package com.wangscaler.factory.factorymethod;


import java.io.BufferedReader;
import java.io.InputStreamReader;

/**
 * @author wangscaler
 * @date 2021.06.22 16:17
 */
public abstract class Order {
    Cook cook = null;
    String type = "";

    abstract Cook createCook(String type);

    public Order() {
        do {
            type = geType();
            cook = createCook(type);
            if (cook != null) {
                cook.prepare();
                cook.cut();
                cook.fry();
                cook.dish();
                cook.serve();
            } else {
                break;
            }
        } while (true);
    }

    private String geType() {
        try {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("请输入您要点的菜,以EOF为结束");
            String str = bufferedReader.readLine();
            return str;
        } catch (Exception e) {
            e.printStackTrace();
            return "";

        }
    }
}

SpicyFactory

package com.wangscaler.factory.factorymethod;

import com.wangscaler.factory.simplefactory.SimpleFactory;

/**
 * @author wangscaler
 * @date 2021.06.23 10:10
 */
public class SpicyFactory extends Order {
    @Override
    Cook createCook(String type) {
        Cook cook = null;
        if (type.equals(SimpleFactory.Menu.FISHFLAVOREDPORK.getName())) {
            cook = new SpicyFishFlavoredPork();
            cook.setName("辣味鱼香肉丝");
        } else if (type.equals(SimpleFactory.Menu.KUNGPAOCHICKEN.getName())) {
            cook = new SpicyKungPaoChicken();
            cook.setName("辣味宫保鸡丁");
        }
        return cook;
    }

    public enum Menu {
        /**
         * 鱼香肉丝
         */
        FISHFLAVOREDPORK("fishflavoredpork"),
        /**
         * 宫保鸡丁
         */
        KUNGPAOCHICKEN("kungpaochicken"),
        /**
         * 结束标识
         */
        EOF("EOF");
        private String name;

        Menu(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

    }
}

NotSpicyFactory

package com.wangscaler.factory.factorymethod;
import com.wangscaler.factory.simplefactory.SimpleFactory;

/**
 * @author wangscaler
 * @date 2021.06.23 10:11
 */
public class NotSpicyFactory extends Order {
    @Override
    Cook createCook(String type) {
        Cook cook = null;
        if (type.equals(SimpleFactory.Menu.FISHFLAVOREDPORK.getName())) {
            cook = new NotSpicyFishFlavoredPork();
            cook.setName("鱼香肉丝");
        } else if (type.equals(SimpleFactory.Menu.KUNGPAOCHICKEN.getName())) {
            cook = new NotSpicyKungPaoChicken();
            cook.setName("宫保鸡丁");
        }
        return cook;
    }

    public enum Menu {
        /**
         * 鱼香肉丝
         */
        FISHFLAVOREDPORK("fishflavoredpork"),
        /**
         * 宫保鸡丁
         */
        KUNGPAOCHICKEN("kungpaochicken"),
        /**
         * 结束标识
         */
        EOF("EOF");
        private String name;

        Menu(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

    }
}

我们可以看到我们在Order增加了抽象的方法createCook,将他的实现去交给子类NotSpicyFactory和SpicyFactory去实现。

抽象工厂模式

抽象工厂模式就是简单工厂模式和工厂方法模式的结合。通俗的话说就是抽象工厂就是生产不同等级产品的工厂族,即既可以生产辣味工厂,又可以生产不辣工厂的工厂。

absfactory.png

保持工厂方法模式中的Cook、NotSpicyFishFlavoredPork、SpicyFishFlavoredPork不变,添加抽象工厂AbsFactory

package com.wangscaler.factory.abstractfactory;

/**
 * @author wangscaler
 * @date 2021.06.23 11:00
 */
public interface AbsFactory {
    public Cook createCook(String type);
}

提供接口,供辣味工厂和不辣工厂实现

SpicyFactory

package com.wangscaler.factory.abstractfactory;



/**
 * @author wangscaler
 * @date 2021.06.23 10:10
 */
public class SpicyFactory implements AbsFactory {

    @Override
    public Cook createCook(String type) {
        Cook cook = null;
        if (type.equals(SpicyFactory.Menu.FISHFLAVOREDPORK.getName())) {
            cook = new SpicyFishFlavoredPork();
            cook.setName("辣味鱼香肉丝");
        } else if (type.equals(SpicyFactory.Menu.KUNGPAOCHICKEN.getName())) {
            cook = new SpicyKungPaoChicken();
            cook.setName("辣味宫保鸡丁");
        }
        return cook;
    }


    public enum Menu {
        /**
         * 鱼香肉丝
         */
        FISHFLAVOREDPORK("fishflavoredpork"),
        /**
         * 宫保鸡丁
         */
        KUNGPAOCHICKEN("kungpaochicken"),
        /**
         * 结束标识
         */
        EOF("EOF");
        private String name;

        Menu(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

    }
}

NotSpicyFactory

package com.wangscaler.factory.abstractfactory;


/**
 * @author wangscaler
 * @date 2021.06.23 10:10
 */
public class NotSpicyFactory implements AbsFactory {

    @Override
    public Cook createCook(String type) {
        Cook cook = null;
        if (type.equals(NotSpicyFactory.Menu.FISHFLAVOREDPORK.getName())) {
            cook = new NotSpicyFishFlavoredPork();
            cook.setName("鱼香肉丝");
        } else if (type.equals(NotSpicyFactory.Menu.KUNGPAOCHICKEN.getName())) {
            cook = new NotSpicyKungPaoChicken();
            cook.setName("宫保鸡丁");
        }
        return cook;
    }


    public enum Menu {
        /**
         * 鱼香肉丝
         */
        FISHFLAVOREDPORK("fishflavoredpork"),
        /**
         * 宫保鸡丁
         */
        KUNGPAOCHICKEN("kungpaochicken"),
        /**
         * 结束标识
         */
        EOF("EOF");
        private String name;

        Menu(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

    }
}

此时我们的Order只需要使用抽象工厂即可

package com.wangscaler.factory.abstractfactory;


import java.io.BufferedReader;
import java.io.InputStreamReader;

/**
 * @author wangscaler
 * @date 2021.06.22 16:17
 */
public class Order {

    AbsFactory factory;

    private void setFactory(AbsFactory factory) {
        Cook cook = null;
        String type = "";
        this.factory = factory;
        do {
            type = geType();
            cook = factory.createCook(type);
            if (cook != null) {
                cook.prepare();
                cook.cut();
                cook.fry();
                cook.dish();
                cook.serve();
            } else {
                break;
            }
        } while (true);
    }

    public Order(AbsFactory factory) {
        setFactory(factory);

    }

    private String geType() {
        try {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("请输入您要点的菜,以EOF为结束");
            String str = bufferedReader.readLine();
            return str;
        } catch (Exception e) {
            e.printStackTrace();
            return "";

        }
    }
}

main函数

package com.wangscaler.factory.abstractfactory;

/**
 * @author wangscaler
 * @date 2021.06.23 11:17
 */
public class AbstractFactory {
    public static void main(String[] args) {
        new Order(new SpicyFactory());
    }

}

这种方式具有良好的扩展性,比如我们增加微辣的口味,只需要添加微辣工厂去实现抽象工厂的createCook方法和添加微辣口味的菜。

源码中的工厂模式

JDK中的Calendar

Calendar calendar=Calendar.getInstance();我们打开Calendar的getInstance方法发现

 /**
     * 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));
    }

    /**
     * Gets a calendar using the specified time zone and default locale.
     * The <code>Calendar</code> returned is based on the current time
     * in the given time zone with the default
     * {@link Locale.Category#FORMAT FORMAT} locale.
     *
     * @param zone the time zone to use
     * @return a Calendar.
     */
    public static Calendar getInstance(TimeZone zone)
    {
        return createCalendar(zone, Locale.getDefault(Locale.Category.FORMAT));
    }

    /**
     * Gets a calendar using the default time zone and specified locale.
     * The <code>Calendar</code> returned is based on the current time
     * in the default time zone with the given locale.
     *
     * @param aLocale the locale for the week data
     * @return a Calendar.
     */
    public static Calendar getInstance(Locale aLocale)
    {
        return createCalendar(TimeZone.getDefault(), aLocale);
    }

    /**
     * Gets a calendar with the specified time zone and locale.
     * The <code>Calendar</code> returned is based on the current time
     * in the given time zone with the given locale.
     *
     * @param zone the time zone to use
     * @param aLocale the locale for the week data
     * @return a Calendar.
     */
    public static Calendar getInstance(TimeZone zone,
                                       Locale aLocale)
    {
        return createCalendar(zone, aLocale);
    }

    private static Calendar createCalendar(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;
    }

他其实调用的createCalendar,而在createCalendar中我们发现

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;

就是我们讲的简单工厂模式

总结

设计模式必须遵守设计原则:依赖抽象而不要依赖于具体,对扩展开放对修改关闭...

工厂模式的四个概念:

  • 抽象产品(Cook):是所有具体产品的父类。

  • 具体产品(KungPaoChicken):继承了Cook的父类,也是工厂生产的对象实例。

  • 抽象工厂(AbsFactory):提供创建对象接口,供子类实现。

  • 具体工厂(SpicyFactory):实现抽象工厂的接口,生产对象实例。 以上三种设计模式可根据业务灵活使用

  • 简单工厂模式

    • 需要创建的对象较少
    • 客户端不需要关注对象创建的过程
  • 工厂方法模式

    • 不知道它所需要的对象的类
    • 通过其子类来指定创建哪个对象
    • 将创建对象的任务委托给多个工厂子类中的某一个
  • 抽象工厂模式

    • 需要一组或者多组对象完成一个功能
    • 不会频繁增加对象
    • 不知道需要的对象的类

参考资料