简单工厂模式和抽象工厂模式
不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