写点什么

【设计模式】第四篇 - 简单工厂

用户头像
Brave
关注
发布于: 刚刚

1,前言

上一篇我们说了单例,按照难度来递进,这次轮到工厂了工厂计划分三部分说:简单工厂,工厂方法,抽象工厂三部分争取使用相似或有关联的例子进行递进式的讲解这样一来,既可以实现三种工厂的对比,又可以说明根据需求变化工厂模式逐渐演变的过程简单工厂实际并不属于一种设计模式,但这里我们就当成一种设计模式来说,主要是给后边两个模式做铺垫
复制代码



2,场景


外卖有各种饭,工厂这部分就用这些饭来做例子,将工厂模式用于制作各种饭的过程

简单工厂:
餐厅制作鸡肉饭,烤肉饭,牛肉饭
制作流程为:准备,烹饪,摆放,打包
复制代码



3,不使用工厂的代码


简单的写一个类,根据传入的类型做饭

package com.brave.food;
import com.brave.food.simplefactory.rice.BarbecueRice;import com.brave.food.simplefactory.rice.BeefRice;import com.brave.food.simplefactory.rice.ChickenRice;import com.brave.food.simplefactory.rice.Rice;
/** * 餐厅 * 不使用设计模式的餐厅 * @author Brave * */public class Restaurant {
public void orderRice(String type){
Rice rice = null;
if(type.equals("BarbecueRice")){ rice = new BarbecueRice(); } else if(type.equals("BeefRice")){ rice = new BeefRice(); } else if(type.equals("ChickenRice")){ rice = new ChickenRice(); }
System.out.println("-----------"+type+"----------");
rice.prepare(); rice.cook(); rice.set(); rice.box(); }}
复制代码



完善剩余代码:


饭的抽象类:

package com.brave.food.simplefactory.rice;
/** * Rice:饭抽象类 * 各种饭继承此抽象类 * @author Brave * */public abstract class Rice {
// 准备 public void prepare(){ System.out.println("准备"); }
// 烹饪 public void cook(){ System.out.println("烹饪"); }
// 摆放 public void set(){ System.out.println("摆放"); }
// 打包 public void box(){ System.out.println("打包"); }}
复制代码


各种饭:继承上边饭的抽象类:

package com.brave.food.simplefactory.rice;
public class ChickenRice extends Rice {
@Override public void prepare() { System.out.println("鸡肉饭-准备"); }
@Override public void cook() { System.out.println("鸡肉饭-烹饪"); }
@Override public void set() { System.out.println("鸡肉饭-摆放"); }
@Override public void box() { System.out.println("鸡肉饭-打包"); }
}
复制代码


客户端:

package com.brave.food;
/** * 客户端 * 不使用设计模式的客户端 * @author Brave * */public class Client {
public static void main(String[] args) {
Restaurant restaurant = new Restaurant();
restaurant.orderRice("BarbecueRice"); restaurant.orderRice("BeefRice"); restaurant.orderRice("ChickenRice");
}
}
复制代码



4,问题和分析


首先,这种写法简单明了,在实际项目中应用也很多,并没有什么问题最大的问题不是代码本身,而是需求的变化
问题: Restaurant类,依赖了所有"饭"的具体类(BarbecueRice,BeefRice,ChickenRice) 当饭的"产品种类"频繁变化时,就需要对这个类进行频繁修改,这个修改可能造成orderRice方法问题,从而影响到其他"饭"的创建
违反了几个设计原则:
1,依赖倒置原则: 依赖于抽象,不要依赖具体类,相比于"针对接口编程,不针对实现编程"来说,这里更强调抽象 不能让高层组件依赖低层组件,并且,无论高层或是底层组件,两者都应依赖于抽象 Restaurant是高层组件,各种"饭"的实现是低层组件,Restaurant依赖了所有"饭"的实现
2,”开放-关闭”原则: 对拓展开放,对修改关闭 频繁维护饭的"产品种类",无法让orderRice()对修改关闭
所以我们应该找出变化的部分,将他们从不变的部分中分离出来
复制代码



5,简单工厂


将频繁变化的部分创建为一个简单工厂类

package com.brave.food.simplefactory;
import com.brave.food.simplefactory.rice.BarbecueRice;import com.brave.food.simplefactory.rice.BeefRice;import com.brave.food.simplefactory.rice.ChickenRice;import com.brave.food.simplefactory.rice.Rice;
/** * 简单工厂 * 用于创建具体对象 * @author Brave * */public class SimpleRiceFactory {
/** * 根据类型创建对应的"饭"对象 * @param type * @return */ public Rice createRice(String type){
Rice rice = null;
if(type.equals("BarbecueRice")){ rice = new BarbecueRice(); } else if(type.equals("BeefRice")){ rice = new BeefRice(); } else if(type.equals("ChickenRice")){ rice = new ChickenRice(); }
return rice; }}
复制代码


修改餐厅的构造方法,使用工厂进行饭的实例化:

package com.brave.food.simplefactory;
import com.brave.food.simplefactory.rice.Rice;
/** * 餐厅 * @author Brave * */public class Restaurant {
SimpleRiceFactory factory;
public Restaurant(SimpleRiceFactory factory){ this.factory = factory; }
public void orderRice(String type){
Rice rice;
rice = factory.createRice(type);
System.out.println("-----------"+type+"----------");
rice.prepare(); rice.cook(); rice.set(); rice.box(); }}
复制代码


客户端:

package com.brave.food.simplefactory;
/** * 客户端: * 仅仅依赖SimpleRiceFactory, Restaurant两个类 * 将各种"饭"的制作流程和实例化过程封装起来 * 当有新的"饭"加入时无需修改原有客户端代码,只需添加调用即可 * @author Brave * */public class Client {
public static void main(String[] args) {
// 创建简单工厂-肯定类型创建"饭"对象 SimpleRiceFactory simpleFactory = new SimpleRiceFactory();
// 注入工厂创建餐厅-使餐厅创建"饭"对象的过程受工厂对象实现的控制 Restaurant restaurant = new Restaurant(simpleFactory);
// 向指定了工厂对象的餐厅下单 restaurant.orderRice("BarbecueRice"); restaurant.orderRice("BeefRice"); restaurant.orderRice("ChickenRice");
}
}
复制代码



6,简单工厂分析


简单工厂将变化的部分提取出来Restaurant依赖Rice(抽象类),SimpleRiceFactory(简单工厂)orderRice()仅需要消费简单工厂返回的对象即可当饭的"产品种类"频繁变化时,Restaurant类无需修改
简单工厂的优点: 去除了客户端与具体产品的依赖,封装了对象的创建过程
简单工厂的的不足: 工厂类中包含了所有实例创建的逻辑,一旦工厂出问题,所有客户端都会有问题 简单工厂的产品是基于共同的抽象类或接口创建的,所以当产品种类增加时(新的抽象类加入),工厂类就需要判断创建何种接口的产品,和创建何种种类的产品相互混淆,违背了单一职责原则,导致系统丧失灵活性和可维护性 新增一个产品时,无法避免的,必须修改工厂类,但可以利用反射在一定程度上解决(仅限于产品类的构造及初始化相同的场景),所以很难满足严格意义上的"开放-关闭"原则
简单工厂还有一种实现方式是使用静态工厂方式,静态工厂虽然不需要实例化具体对象就可以使用,但丧失了通过继承改变行为的能力
复制代码



7,利用反射弥补简单工厂拓展性

利用反射弥补简单工厂拓展性,从而在某种程度上实现"开放-关闭"原则
复制代码


1,修改工厂类,通过反射创建对象

package com.brave.food.simplefactory.reflection;
import com.brave.food.simplefactory.rice.Rice;
/** * 简单工厂-反射实现 * 利用反射弥补简单工厂拓展性,从而在某种程度上实现”开放-关闭”原则 * * @author Brave * */public class SimpleRiceFactory {
/** * 根据类型创建对应的"饭"对象 * @param c * @return */ public Rice createRice(Class c){
Rice rice = null;
try { rice = (Rice) Class.forName(c.getName()).newInstance(); } catch (InstantiationException e) { System.out.println("不支持抽象类或接口"); e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); System.out.println("没有足够权限,即不能访问私有对象"); } catch (ClassNotFoundException e) { System.out.println("类不存在"); e.printStackTrace(); }
return rice; }}
复制代码


修改调用:

package com.brave.food.simplefactory.reflection;
import com.brave.food.simplefactory.rice.Rice;
/** * 餐厅 * @author Brave * */public class Restaurant {
SimpleRiceFactory factory;
public Restaurant(SimpleRiceFactory factory){ this.factory = factory; }
public void orderRice(Class c){
Rice rice;
rice = factory.createRice(c);
System.out.println("-----------"+c+"----------");
rice.prepare(); rice.cook(); rice.set(); rice.box(); }}
复制代码


package com.brave.food.simplefactory.reflection;
import com.brave.food.simplefactory.rice.BarbecueRice;import com.brave.food.simplefactory.rice.BeefRice;import com.brave.food.simplefactory.rice.ChickenRice;
/** * 客户端: * 使用简单工厂-反射的方式实现对象的创建 * 当有新的"饭"加入时,只需添加新的类,无需修改工厂类 * 利用反射弥补简单工厂拓展性,从而在某种程度上实现”开放-关闭”原则 * @author Brave * */public class Client {
public static void main(String[] args) {
// 创建简单工厂-肯定类型创建"饭"对象 SimpleRiceFactory simpleFactory = new SimpleRiceFactory();
// 注入工厂创建餐厅-使餐厅创建"饭"对象的过程受工厂对象实现的控制 Restaurant restaurant = new Restaurant(simpleFactory);
// 向指定了工厂对象的餐厅下单 restaurant.orderRice(BarbecueRice.class); restaurant.orderRice(BeefRice.class); restaurant.orderRice(ChickenRice.class);
}
}
复制代码


运行结果:

-----------class com.brave.food.simplefactory.rice.BarbecueRice----------烤肉饭-准备烤肉饭-烹饪烤肉饭-摆放烤肉饭-打包-----------class com.brave.food.simplefactory.rice.BeefRice----------牛肉饭-准备牛肉饭-烹饪牛肉饭-摆放牛肉饭-打包-----------class com.brave.food.simplefactory.rice.ChickenRice----------鸡肉饭-准备鸡肉饭-烹饪鸡肉饭-摆放鸡肉饭-打包
复制代码



8,简单工厂在 JDK 中的应用

简单工厂在JDK中最典型的应用就是JDBC把关系型数据库认为是抽象产品MySQL,Oracle是具体产品DriverManager是工厂类应用程序通过JDBC接口使用关系型数据库,不需关心使用哪种数据库直接使用DriverManager的静态方法去得到该数据库的Connection即可
复制代码


package com.brave.simplefactory.jdk;
import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;import java.sql.SQLException;
/** * 简单工厂在JDK中的使用 * * @author Brave * */public class JDBC {
public static void main(String[] args) {
Connection conn = null; try { Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1/student"); PreparedStatement ps = conn.prepareStatement("select * from mysql.test"); ps.execute(); } catch (SQLException ex) { System.out.println("Execute query failed " + ex); } catch (ClassNotFoundException e) { System.out.println("Load mysql driver failed " + e); } finally { if (conn != null) { try { conn.close(); } catch (SQLException e) { } } }
}}
复制代码


用户头像

Brave

关注

还未添加个人签名 2018.12.13 加入

还未添加个人简介

评论

发布
暂无评论
【设计模式】第四篇 - 简单工厂