簡單工廠模式和抽象工廠模式
不BB,直接來需求設計
一個披薩店現在需要生產多種顏色的披薩供顧客使用,我們爲了滿足生產多種顏色的披薩生產,腦海裏面第一想法是使用工廠模式來創建不同的披薩。
先看草圖的大致設計
那麼想要圖中的功能。至少需要3個類。一個是綠顏色披薩實體類,紅顏色披薩實體類,還有一個披薩工廠類(我這裏將披薩的實體類做了一下繼承,多加了一個類)。
披薩擁有名字,顏色,價格,描述
@Data
註解來自於lombok,可以減少set,get,toString方法的編寫,但是還是能夠使用這些方法
package cn.withme.pattern.abstractFactory.simple;
import lombok.Data;
import java.util.List;
@Data
public class Pissa {
protected String name ;
protected List<String> colors;
protected Double price;
public String getDesc () {
return null;
}
}
package cn.withme.pattern.abstractFactory.simple;
import cn.withme.pattern.abstractFactory.enums.ColorEnum;
import lombok.Data;
import java.util.Arrays;
/**
* @className: GreenPissa
* @description: 綠色披薩
* @author: liming
* @date: 2020/1/16
**/
@Data
public class GreenPissa extends Pissa{
public GreenPissa() {
this.colors = Arrays.asList(ColorEnum.GREEN.getDesc());
this.name = "一個包含"+this.colors+"的披薩";
this.price = 10.0;
}
@Override
public String getDesc () {
return this.name+",\t 價格:"+this.price;
}
}
package cn.withme.pattern.abstractFactory.simple;
import cn.withme.pattern.abstractFactory.enums.ColorEnum;
import lombok.Data;
import java.util.Arrays;
/**
* @className: GreenPissa
* @description: 綠色披薩
* @author: liming
* @date: 2020/1/16
**/
@Data
public class RedPissa extends Pissa{
public RedPissa() {
this.colors = Arrays.asList(ColorEnum.RED.getDesc());
this.name = "一個包含"+this.colors+"的披薩";
this.price = 10.0;
}
@Override
public String getDesc () {
return this.name+",\t 價格:"+this.price;
}
}
package cn.withme.pattern.abstractFactory.enums;
/**
* @Description: 顏色枚舉類
* @Author: liming
* @date 2020年01月16日
*/
public enum ColorEnum {
RED("red","紅色"),
GREEN("green","綠色"),
;
private String code;
private String desc ;
ColorEnum(String code, String desc) {
this.code = code;
this.desc = desc;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public static String getTypeNameByType (String type){
for (ColorEnum c : ColorEnum.values()) {
if (c.getCode().equalsIgnoreCase(type)) {
return c.getDesc();
}
}
return null;
}
}
披薩生產工廠,這裏通過create
方法,可以生成不同顏色的披薩
package cn.withme.pattern.abstractFactory.simple;
/**
* @className: PissaFactory
* @description: 簡單的披薩生產工廠
* @author: liming
* @date: 2020/1/16
**/
public class PissaFactory {
public Pissa create(String type) {
Pissa p;
switch (type) {
case "green":
p = new GreenPissa();
break;
case "red":
p = new RedPissa();
break;
//省略n個case
default:
p = new Pissa();
}
return p;
}
}
測試一下
package cn.withme.pattern.abstractFactory.simple;
/**
* @className: SimplePissaTest
* @description: 披薩測試
* @author: liming
* @date: 2020/1/16
**/
public class SimplePissaTest {
public static void main(String[] args) {
PissaFactory factory = new PissaFactory();
Pissa pissa = factory.create("red");
print(pissa); //一個包含[紅色]的披薩, 價格:10.0
}
private static void print(Pissa pissa) {
System.out.println(null == pissa ? "" :pissa.getDesc());
}
}
那麼最簡單的披薩生產工廠就已經實現了。
如果覺得if lese 和 switch case
,那麼可以將PissaFactory
修改一下。
package cn.withme.pattern.abstractFactory.simple;
import java.util.HashMap;
import java.util.Map;
/**
* @className: PissaFactory
* @description: 簡單的披薩生產工廠
* @author: liming
* @date: 2020/1/16
**/
public class PissaFactory {
private static final Map<String,Pissa> pissaMap = new HashMap<>();
static {
pissaMap.put("green", new GreenPissa());
pissaMap.put("red", new RedPissa());
}
public Pissa create(String type) {
return pissaMap.get(type);
}
}
這樣就省略了if lese 和 switch case
。
後面披薩店的生意越來越好,全國許多加盟商都來投資。
於是又開始生產披薩。
由於加盟商越來越多,原來的披薩店不能滿足各地的顧客需求。所以加盟商們想在自己的區域可以加一些自己的東西。
於是乎苦逼的程序員們開始加班改。
如果單純的修改這一塊的代碼
switch (type) {
case "green":
p = new GreenPissa();
break;
case "red":
p = new RedPissa();
break;
//省略n個case
default:
p = new Pissa();
}
那麼勢必這一塊的代碼會越來越多,而且如果有長沙的加盟商生產紅色的披薩,但是需要打上"長沙"兩個字標籤,廣州的加盟商生產紅色的披薩,但是需要打上"廣州"兩個字標籤。
目前的設計已經不能滿足披薩的生產需求了。
因爲原來的披薩生產工廠範圍太大了。所以現在必須所有披薩生產的範圍,簡而言之就是某個地區的加盟商就生產自己那個地區的披薩。互不影響纔對。
理想設計如下:
由於需要帶上標籤。所以加一個標籤枚舉類
package cn.withme.pattern.abstractFactory.enums;
/**
* @Description: 位置枚舉類
* @Author: liming
* @date 2020年01月16日
*/
public enum LocationEnum {
CHANGSHA("changsha","長沙"),
GUANGZHOU("guangzhou","廣州"),
;
private String code;
private String desc ;
LocationEnum(String code, String desc) {
this.code = code;
this.desc = desc;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public static String getTypeNameByType (String type){
for (LocationEnum c : LocationEnum.values()) {
if (c.getCode().equalsIgnoreCase(type)) {
return c.getDesc();
}
}
return null;
}
}
//這個類 是 長沙加盟商和廣東加盟商的父類,如果後續有其他的加盟商加進來,可以直接繼承這個類就好了
package cn.withme.pattern.abstractFactory.advance.factory;
import cn.withme.pattern.abstractFactory.advance.domain.Pissa;
/**
* @className: PissaAbstractFactory
* @description: 抽象披薩工廠
* @author: liming
* @date: 2020/1/16
**/
public abstract class PissaAbstractFactory {
abstract Pissa create(String type);
}
長沙加盟商和廣東加盟商工廠如下:
package cn.withme.pattern.abstractFactory.advance.factory;
import cn.withme.pattern.abstractFactory.advance.domain.ChangshaGreenPissa;
import cn.withme.pattern.abstractFactory.advance.domain.ChangshaRedPissa;
import cn.withme.pattern.abstractFactory.advance.domain.Pissa;
import cn.withme.pattern.abstractFactory.enums.LocationEnum;
/**
* @className: ChangshaPissaFactory
* @description:
* @author: liming
* @date: 2020/1/16
**/
public class ChangshaPissaFactory extends PissaAbstractFactory{
public static String LOCALTION = LocationEnum.CHANGSHA.getDesc();
@Override
Pissa create(String type) {
Pissa p;
switch (type) {
case "green":
p = new ChangshaGreenPissa();
break;
case "red":
p = new ChangshaRedPissa();
break;
//省略n個case
default:
p = new Pissa();
}
return p;
}
}
package cn.withme.pattern.abstractFactory.advance.factory;
import cn.withme.pattern.abstractFactory.advance.domain.GuangzhouGreenPissa;
import cn.withme.pattern.abstractFactory.advance.domain.GuangzhouRedPissa;
import cn.withme.pattern.abstractFactory.advance.domain.Pissa;
import cn.withme.pattern.abstractFactory.enums.LocationEnum;
/**
* @className: ChangshaPissaFactory
* @description:
* @author: liming
* @date: 2020/1/16
**/
public class GuangZhouPissaFactory extends PissaAbstractFactory{
public static String LOCALTION = LocationEnum.GUANGZHOU.getDesc();
@Override
Pissa create(String type) {
Pissa p;
switch (type) {
case "green":
p = new GuangzhouGreenPissa();
break;
case "red":
p = new GuangzhouRedPissa();
break;
//省略n個case
default:
p = new Pissa();
}
return p;
}
}
原來的PissaFactory
改成:
package cn.withme.pattern.abstractFactory.advance.factory;
import cn.withme.pattern.abstractFactory.advance.domain.Pissa;
/**
* @className: PissaFactory
* @description: 簡單的披薩生產工廠
* @author: liming
* @date: 2020/1/16
**/
public class PissaFactory {
private PissaAbstractFactory abstractFactory;
public PissaFactory(PissaAbstractFactory abstractFactory) {
this.abstractFactory = abstractFactory;
}
public Pissa create(String type) {
Pissa pissa = abstractFactory.create(type);
return pissa;
}
}
ChangshaGreenPissa,ChangshaRedPissa,GuangzhouGreenPissa,GuangzhouRedPissa
這幾個類變化不大,貼一個類,其餘的差不多
package cn.withme.pattern.abstractFactory.advance.domain;
import cn.withme.pattern.abstractFactory.advance.factory.ChangshaPissaFactory;
import cn.withme.pattern.abstractFactory.enums.ColorEnum;
import lombok.Data;
import java.util.Arrays;
/**
* @className: GreenPissa
* @description: 綠色披薩
* @author: liming
* @date: 2020/1/16
**/
@Data
public class ChangshaGreenPissa extends Pissa{
public ChangshaGreenPissa() {
this.colors = Arrays.asList(ColorEnum.GREEN.getDesc());
this.name = "一個包含"+this.colors+"的披薩";
this.price = 10.0;
}
@Override
public String getDesc () {
return this.name+",\t 價格:"+this.price+",\t 生產地點位於:" + ChangshaPissaFactory.LOCALTION;
//這裏增加了生產位置
}
}
那麼這麼一改造的話。後續來再多的加盟商都可以適配這個模式。
寫一個測試類試下:
package cn.withme.pattern.abstractFactory.advance;
import cn.withme.pattern.abstractFactory.advance.domain.Pissa;
import cn.withme.pattern.abstractFactory.advance.factory.GuangZhouPissaFactory;
import cn.withme.pattern.abstractFactory.advance.factory.PissaFactory;
/**
* @className: AdvancePissaFactory
* @description: 測試類
* @author: liming
* @date: 2020/1/16
**/
public class AdvancePissaFactoryTest {
public static void main(String[] args) {
PissaFactory pissaFactory = new PissaFactory(new GuangZhouPissaFactory());
Pissa pissa = pissaFactory.create("red");
System.out.println(pissa.getDesc());
}
}
這樣就能改最小的代碼,又可以適配不同的加盟商(產品)。
總結一下:簡單工廠模式和抽象工廠模式的區別
簡單工廠模式對於生成少量的對象來說有好處,但是沒有辦法做區分多產品(如果要在簡單工廠模式裏面區分產品也是可以的,只不過會帶來更多的if else
。
而且魯迅說過,太多的人修改同一個類的話,肯定會把別人的改錯,如果每一個加盟商只維護自己的那一部分功能,出錯的概率也會相對減少,影響面也不會擴散到其他的加盟商)
代碼已上傳github
(github主頁:https://github.com/q920447939/java-study)
代碼地址:https://github.com/q920447939/java-study/tree/master/java-bases/src/main/java/cn/withme/pattern/abstractFactory