简单工厂模式
需求
看一个披萨的项目:
- 披萨的种类有很多(GePizz, CePizz等)
- 披萨的制作步骤有 prepare, bake, cut, box
- 完成披萨店的订购功能
需求点: 要便于披萨种类的扩展, 要便于维护
类图
传统方式实现
代码语言:javascript复制package com.dance.design.designmodel.factory.simple;
public class CtPizz {
public static void main(String[] args) {
OrderPizza.ding("ce");
OrderPizza.ding("ge");
}
}
/**
* 披萨类
*/
abstract class Pizza{
/**
* 名称
*/
public String name;
/**
* 准备材料
*/
public abstract void prepare();
/**
* 为了不每次都全部调用, 我创建一个构建披萨的方法
*/
public void buildPizza(){
prepare();
bake();
cut();
box();
}
/**
* 烘烤
*/
public void bake(){
System.out.println(name "baking ...");
}
/**
* 切块
*/
public void cut(){
System.out.println(name "cutting ...");
}
/**
* 打包
*/
public void box(){
System.out.println(name "boxing ...");
}
}
/**
* 奶酪披萨
*/
class CePizza extends Pizza{
@Override
public void prepare() {
System.out.println("准备奶酪披萨的原材料");
name = "奶酪披萨";
}
}
/**
* 希腊披萨
*/
class GePizza extends Pizza{
@Override
public void prepare() {
System.out.println("准备希腊披萨的原材料");
name = "希腊披萨";
}
}
/**
* 订购类
*/
class OrderPizza{
public OrderPizza(){}
public static void ding(String orderType){
Pizza pizza = null;
if("ce".equals(orderType)){
pizza = new CePizza();
}else if("ge".equals(orderType)){
pizza = new GePizza();
}else{
throw new RuntimeException("订单类型错误");
}
// 开始制作
pizza.buildPizza();
}
}
我这里没有写设么用户输入啥的,应为感觉意义不大, 我们主要看设计模式,而不是关注其他的边缘细节
传统实现方式分析
- 优点是比较好理解, 简单容易操作
- 缺点是违反了Ocp原则, 即对扩展开放,对修改关闭, 当我们给类增加新功能的时候尽量不修改代码,或者尽可能的少修改代码
需求改进
我们需要新增一种Pizza, 我们就需要创建新的Pizza种类 ,然后修改订单类的判断逻辑, 当然, 如果在别处还有创建的话, 需要在别处也修改, 这样的话改动量就会比较大
传统方式改进
我们新增巧克力披萨
代码语言:javascript复制/**
* 巧克力披萨
*/
class QkPizza extends Pizza{
@Override
public void prepare() {
System.out.println("准备巧克力披萨的原材料");
name = "巧克力披萨";
}
}
修改OrderPizza逻辑, 这个创建的逻辑可能在很多地方都有,所以需要都修改, 我们这里只有一种效果看着还不是很麻烦
增加调用逻辑
到此扩展完毕, 应为设计的原因披萨类型扩展很容易, 调用扩展是必须的, 但是中间部分如果很多的话, 其实是可以统一管理的, 还有就是对于OrderPizza来说, 它其实是不应该知道Pizza的创建逻辑的, 而且如果后续扩展门店的话
会有orderPizza2, orderPizza3, 这样每个类都会和Pizza,Ce.., Ge.. Qk..全部发生关系, 如果一旦再增加Pizza的种类, 就会改动量非常大
简单工厂改进
基本介绍
- 简单工厂模式是属于创建型模式,是工厂模式的一种, 简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例, 简单工厂模式是工厂模式中最简单实用的
- 简单工厂模式: 定义一个创建对象的类, 由这个类来统一对实例进行创建
- 在软件开发中, 我们会用到大量的创建某种, 某类或者某批对象时, 就会使用到工厂模式
改进思路
把创建具体披萨的任务交给工厂, 这样我们创建的时候, 只需要调用工厂的方法即可, 其调用工厂的地方是不需要修改的
改进前类图
改进后类图
这样, 我让我的订单去面对工厂, 后续改造仅限于工厂, 种类, 和调用方, 但是订单是不需要修改的, 哪怕后续扩展订单门店, 也是直接面对工厂就可以了, 不必关注细节
代码
增加工厂类
代码语言:javascript复制class PizzaFactory{
public static Pizza createPizza(String orderType){
Pizza pizza = null;
if("ce".equals(orderType)){
pizza = new CePizza();
}else if("qk".equals(orderType)){
pizza = new QkPizza();
}else if("ge".equals(orderType)){
pizza = new GePizza();
}else{
throw new RuntimeException("订单类型错误");
}
return pizza;
}
}
修改OrderPizza
代码语言:javascript复制/**
* 订购类
*/
class OrderPizza{
public OrderPizza(){}
public static void ding(String orderType){
// 开始制作
PizzaFactory.createPizza(orderType).buildPizza();
}
}
后续种类再则么多, 我的订单类都是不用动的
工厂方法模式
需求演化
我们的披萨店越做越大, 终于,在不同的地区开分店了,比如北京, 上海,广州等, 这个时候顾客就可以在不同的地区点北京的奶酪披萨, 上海的藤椒披萨等
思路
使用简单工厂模式, 我们可以创建BJPizzaFactory, SHPizzaFactory等, 从这案例来说是可以的, 但是考虑项目的规模,以为软件的可维护性和可扩展性并不是特别好, 所以我们用工厂方法模式
工厂方法模式介绍
工厂方法设计方案: 将披萨项目的实例化功能抽象成抽象方法, 在不同的口味点餐子类中做具体实现
工厂方法模式: 定义了一个创建对象的抽象方法, 由子类决定要实例化的类, 工厂方法模式将对象的实例化推迟到子类
类图
代码
代码语言:javascript复制package com.dance.design.designmodel.factory.simple.sp3;
import com.sun.org.apache.xpath.internal.operations.Or;
public class CtPizz {
public static void main(String[] args) {
new BJOrderPizza().ding("ce");
new BJOrderPizza().ding("ge");
new SHOrderPizza().ding("ce");
new SHOrderPizza().ding("ge");
}
}
/**
* 披萨类
*/
abstract class Pizza{
/**
* 名称
*/
public String name;
/**
* 准备材料
*/
public abstract void prepare();
/**
* 为了不每次都全部调用, 我创建一个构建披萨的方法
*/
public void buildPizza(){
prepare();
bake();
cut();
box();
}
/**
* 烘烤
*/
public void bake(){
System.out.println(name "baking ...");
}
/**
* 切块
*/
public void cut(){
System.out.println(name "cutting ...");
}
/**
* 打包
*/
public void box(){
System.out.println(name "boxing ...");
}
}
/**
* 北京奶酪披萨
*/
class BJCePizza extends Pizza{
@Override
public void prepare() {
System.out.println("准备北京奶酪披萨的原材料");
name = "北京奶酪披萨";
}
}
/**
* 上海奶酪披萨
*/
class SHCePizza extends Pizza{
@Override
public void prepare() {
System.out.println("准备上海奶酪披萨的原材料");
name = "上海奶酪披萨";
}
}
/**
* 希腊披萨
*/
class BJGePizza extends Pizza{
@Override
public void prepare() {
System.out.println("准备北京希腊披萨的原材料");
name = "北京希腊披萨";
}
}
/**
* 希腊披萨
*/
class SHGePizza extends Pizza{
@Override
public void prepare() {
System.out.println("准备上海希腊披萨的原材料");
name = "上海希腊披萨";
}
}
/**
* 订购类
*/
abstract class OrderPizza{
public OrderPizza(){}
public abstract void ding(String orderType);
}
/**
* 北京店
*/
class BJOrderPizza extends OrderPizza {
@Override
public void ding(String orderType) {
Pizza pizza = null;
if("ce".equals(orderType)){
pizza = new BJCePizza();
}else if("ge".equals(orderType)){
pizza = new BJCePizza();
}else{
throw new RuntimeException("订单类型错误");
}
// 开始制作
pizza.buildPizza();
}
}
/**
* 上海店
*/
class SHOrderPizza extends OrderPizza {
@Override
public void ding(String orderType) {
Pizza pizza = null;
if("ce".equals(orderType)){
pizza = new SHCePizza();
}else if("ge".equals(orderType)){
pizza = new SHCePizza();
}else{
throw new RuntimeException("订单类型错误");
}
// 开始制作
pizza.buildPizza();
}
}
这样我们调用只针对与门店, 上海调用上海的门店, 北京调用北京的门店, 然后由门店去面对Pizza和子类, 但是我感觉这样也不太好, 接下来使用抽象工厂改造~
抽象工厂模式
基本介绍
- 抽象工厂模式:定义了一个interface用于创建相关或有依赖关系的对象簇,而无需指明具体的类
- 抽象工厂模式可以将简单工厂和工厂方法模式进行整合
- 从设计层面看, 抽象工厂模式就是对简单工厂模式的改进(或者称之为进一步抽象)
- 将工厂抽象成两层, ABSFactory(抽象工厂), 和 具体实现的工厂子类, 程序员可以根据创建对象类型使用对应的工厂子类, 这样将单个的简单工厂类变成了工厂簇, 更利于代码的维护和扩展
类图
订单抽象类, 面向 抽象工厂, 抽象工厂面向披萨抽象类, 细节由实现类去维护
代码
代码语言:javascript复制package com.dance.design.designmodel.factory.simple.sp3;
public class CtPizz {
public static void main(String[] args) {
new BJOrderPizza(new BJPizzaFactory()).ding("ce");
new BJOrderPizza(new BJPizzaFactory()).ding("ge");
new SHOrderPizza(new SHPizzaFactory()).ding("ce");
new SHOrderPizza(new SHPizzaFactory()).ding("ge");
}
}
/**
* 披萨类
*/
abstract class Pizza{
public String name;
public abstract void prepare();
public void buildPizza(){
prepare();
bake();
cut();
box();
}
public void bake(){
System.out.println(name "baking ...");
}
public void cut(){
System.out.println(name "cutting ...");
}
public void box(){
System.out.println(name "boxing ...");
}
}
/**
* 北京奶酪披萨
*/
class BJCePizza extends Pizza{
@Override
public void prepare() {
System.out.println("准备北京奶酪披萨的原材料");
name = "北京奶酪披萨";
}
}
/**
* 上海奶酪披萨
*/
class SHCePizza extends Pizza{
@Override
public void prepare() {
System.out.println("准备上海奶酪披萨的原材料");
name = "上海奶酪披萨";
}
}
/**
* 北京希腊披萨
*/
class BJGePizza extends Pizza{
@Override
public void prepare() {
System.out.println("准备北京希腊披萨的原材料");
name = "北京希腊披萨";
}
}
/**
* 上海希腊披萨
*/
class SHGePizza extends Pizza{
@Override
public void prepare() {
System.out.println("准备上海希腊披萨的原材料");
name = "上海希腊披萨";
}
}
/**
* 订购类
*/
abstract class OrderPizza{
/**
* 面对抽象工厂
*/
protected AbsPizzaFactory pizzaFactory;
public OrderPizza(AbsPizzaFactory pizzaFactory){
this.pizzaFactory = pizzaFactory;
}
public void ding(String orderType){
// 默认逻辑
pizzaFactory.createPizza(orderType).buildPizza();
}
}
/**
* 抽象工厂
*/
abstract class AbsPizzaFactory{
protected abstract Pizza createPizza(String orderType);
}
/**
* 北京披萨工厂
*/
class BJPizzaFactory extends AbsPizzaFactory{
@Override
protected Pizza createPizza(String orderType) {
Pizza pizza = null;
if("ce".equals(orderType)){
pizza = new BJCePizza();
}else if("ge".equals(orderType)){
pizza = new BJCePizza();
}else{
throw new RuntimeException("订单类型错误");
}
return pizza;
}
}
/**
* 上海披萨工厂
*/
class SHPizzaFactory extends AbsPizzaFactory{
@Override
protected Pizza createPizza(String orderType) {
Pizza pizza = null;
if("ce".equals(orderType)){
pizza = new SHCePizza();
}else if("ge".equals(orderType)){
pizza = new SHCePizza();
}else{
throw new RuntimeException("订单类型错误");
}
return pizza;
}
}
/**
* 北京店
*/
class BJOrderPizza extends OrderPizza {
public BJOrderPizza(AbsPizzaFactory pizzaFactory) {
super(pizzaFactory);
}
}
/**
* 上海店
*/
class SHOrderPizza extends OrderPizza {
public SHOrderPizza(AbsPizzaFactory pizzaFactory) {
super(pizzaFactory);
}
}
其实抽象工厂我感觉还可以改进一下, 这样的话调用方就知道了具体的类,如果调用方多的话也不好维护, 可以再提供一个工厂创建者的单利, 通过传入地区也就是BJ, SH来获取一个抽象工厂(都已经学过单利模式了吧), 这个就自己去改造吧
源码剖析
JDK中的工厂模式
- JDK中的Calendar类中, 就使用了简单工厂模式
测试
代码语言:javascript复制public class CalendarTest {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
System.out.println("年:" calendar.get(Calendar.YEAR));
// 月从0 开始 需要 1
System.out.println("月:" (calendar.get(Calendar.MONTH) 1));
System.out.println("日:" calendar.get(Calendar.DAY_OF_MONTH));
System.out.println("时:" calendar.get(Calendar.HOUR_OF_DAY));
System.out.println("分:" calendar.get(Calendar.MINUTE));
System.out.println("秒:" calendar.get(Calendar.SECOND));
}
}
如何,有感受到简单工厂的气息吗? 当然不只是简单工厂, 从第一行getInstance中也感受出来了吧, 这还是一个单利, 当然如果看源码的的话, 你就感受到很多其他的设计模式, 由此可以看出大佬是多么的**
源码
emm, 源码就不看了, 从使用上感受一下就可以了, 额, 要不还是看一下吧
代码语言:javascript复制public static Calendar getInstance(TimeZone zone,Locale aLocale){
return createCalendar(zone, aLocale);
}
getInstance调用了createCalendar
代码语言:javascript复制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;
}
好了看完了吧
工厂模式小结
工厂模式的意义
将实例化细节的代码提取出来, 放到工厂类中统一管理和维护,达到和主业务线的依赖关系的解耦,从而提高了项目的扩展和维护性
三种模式
- 简单工厂模式
- 工厂方法模式
- 抽象工厂模式
设计模式的依赖抽象原则
- 创建对象的依赖实例时, 不要直接new 类,而是吧这个new 类的动作放在一个工厂方法中,并返回, 有的书上说变量不要直接持有具体类的引用
- 不要让类继承具体类,而是继承抽象类 或者实现接口
- 不要覆盖基类中已经实现的方法