这是我参与更文挑战的第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)。代码如下
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去实现。
抽象工厂模式
抽象工厂模式就是简单工厂模式和工厂方法模式的结合。通俗的话说就是抽象工厂就是生产不同等级产品的工厂族,即既可以生产辣味工厂,又可以生产不辣工厂的工厂。
保持工厂方法模式中的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):实现抽象工厂的接口,生产对象实例。 以上三种设计模式可根据业务灵活使用
-
简单工厂模式
- 需要创建的对象较少
- 客户端不需要关注对象创建的过程
-
工厂方法模式
- 不知道它所需要的对象的类
- 通过其子类来指定创建哪个对象
- 将创建对象的任务委托给多个工厂子类中的某一个
-
抽象工厂模式
- 需要一组或者多组对象完成一个功能
- 不会频繁增加对象
- 不知道需要的对象的类