需求:设计一个披萨订购系统。
设计一个抽象披萨类(Coffee),并定义其两个子类(奶酪披萨【CheessPizza】和水果披萨【FruitPizza】);再设计一个披萨店类(PizzaStore),披萨店具有点披萨的功能。
类图如下:
代码如下:
Pizza类:
package d3_factory.t1_simple.before;
/**
* 披萨类 - 抽象
* @author luxianghai
*
*/
public abstract class Pizza {
// 咖啡名
protected String name;
// 准备工作
protected abstract void prepare();
// 烘烤
protected void bake() {
System.out.println(name + " - 烘烤");
}
protected void cut() {
System.out.println(name + " - 切割");
}
protected void box() {
System.out.println(name + " - 打包");
}
}
CheesePizza类
package d3_factory.t1_simple.before;
/**
* 奶酪披萨
* @author luxianghai
*
*/
public class CheesePizza extends Pizza {
public CheesePizza() {
super.name = "奶酪披萨";
}
@Override
protected void prepare() {
System.out.println(super.name);
System.out.println("准备奶酪中...");
}
}
FruitPizza类
package d3_factory.t1_simple.before;
/**
* 水果披萨
* @author luxianghai
*
*/
public class FruitPizza extends Pizza {
public FruitPizza() {
super.name = "水果披萨";
}
@Override
protected void prepare() {
System.out.println(super.name);
System.out.println("准备水果中...");
}
}
PizzaSotre类
package d3_factory.t1_simple.before;
import java.util.Scanner;
/**
* 披萨店
* @author luxianghai
*
*/
public class PizzaStore {
public PizzaStore() {
orderPizza();
}
/**
* 点披萨
*/
private void orderPizza() {
Pizza pizza = null;
String type = ""; // 披萨类型
do {
type = getType();
if ( "奶酪披萨".equals(type) ) {
pizza = new CheesePizza();
} else if ( "水果披萨".equals(type) ) {
pizza = new FruitPizza();
} else {
System.out.println("退出!!!");
break;
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} while(true);
}
/**
* 获取披萨类型
* @return
*/
private String getType() {
System.out.println("输入披萨名:");
Scanner scan = new Scanner(System.in);
return scan.next();
}
}
测试
package d3_factory.t1_simple.before;
public class Client {
public static void main(String[] args) {
new PizzaStore();
}
}
优缺点分析
改进思路分析:
简单工厂不是一种设计模式,反而比较像是一种编程习惯。
简单工厂模式的结构:
使用简单工厂对 5.1小节 中的案例进行改进,我们只需对PizzaStore做修改(把创建对象的语句提取到工厂中),并创建一个简单工厂SimpleFactory类即可,代码如下:
SimpleFactory.java
package d3_factory.t1_simple.after1;
/**
* 简单工厂类
* @author luxianghai
*
*/
public class SimpleFactory {
public Pizza createPizza(String type) {
Pizza pizza = null;
if ( "奶酪披萨".equals(type) ) {
pizza = new CheesePizza();
} else if ( "水果披萨".equals(type) ) {
pizza = new FruitPizza();
}
return pizza;
}
}
PizzaStore
package d3_factory.t1_simple.after2;
import java.util.Scanner;
/**
* 披萨店
* @author luxianghai
*
*/
public class PizzaStore {
public PizzaStore() {
orderPizza();
}
/**
* 点披萨
*/
private void orderPizza() {
String type = ""; // 披萨类型
do {
type = getType();
Pizza pizza = SimpleFactory.createPizza(type);
if(null != pizza) {
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} else {
System.out.println("退出!!!");
break;
}
} while(true);
}
/**
* 获取披萨类型
* @return
*/
private String getType() {
String type = "";
Scanner scan = null;
try {
System.out.println("输入披萨名:");
scan = new Scanner(System.in);
type = scan.next();
} finally {
if ( null != scan ) {
scan.close();
}
}
return type;
}
}
此时如果我们新增一个鸡肉披萨,只需创建新类ChickenPizza,然后在SimpleFactory添加一个判断即可,就不需要修改客户端了,更容易扩展了:
ChickenPizza
package d3_factory.t1_simple.after2;
/**
* 鸡肉披萨
* @author luxianghai
*
*/
public class ChickenPizza extends Pizza {
public ChickenPizza() {
super.name = "鸡肉披萨";
}
@Override
protected void prepare() {
System.out.println("鸡肉披萨");
System.out.println("准备鸡肉中...");
}
}
SimpleFactory
package d3_factory.t1_simple.after2;
/**
* 简单工厂类
* @author luxianghai
*
*/
public class SimpleFactory {
public static Pizza createPizza(String type) {
Pizza pizza = null;
if ( "奶酪披萨".equals(type) ) {
pizza = new CheesePizza();
} else if ( "水果披萨".equals(type) ) {
pizza = new FruitPizza();
} else if ( "鸡肉披萨".equals(type) ) {
pizza = new FruitPizza();
}
return pizza;
}
}
如此边添加了一个新的披萨种类
优点:封装了创建对象的过程,可以通过参数直接获取对象。把对象的创建和业务逻辑层分开,这样以后就避免了修改客户代码,如果要实现新产品直接修改工厂类,而不需要在原代码中修改,这样就降低了客户代码修改的可能性,更加容易扩展。
缺点:增加新产品时还是需要修改工厂类的代码,违背了“开闭原则”
定义一个用于创建对象的接口或抽象类,让子类决定实例化哪个产品类对象。工厂方法使一个产品类的实例化延迟到其工厂的子类。
工厂方法模式的主要角色:
披萨项目新的需求:客户在点披萨时,可以根据所在地点不同的披萨,比如 北京的水果pizza、北京的奶酪pizza 或者是伦敦的奶酪pizza、伦敦的水果pizza。
思路1:使用简单工厂模式,创建不同的简单工厂类,比如BJPizzaSimpleFactory、LDPizzaSimpleFactory 等等.从当前这个案例来说,也是可以的,但是考虑到项目的规模,以及软件的可维护性、可扩展性并不是特别好
思路2:使用工厂方法模式
类图如下:
代码如下:
抽象产品
/**
* 披萨类 - 抽象产品
* @author luxianghai
*
*/
public abstract class Pizza {
// 咖啡名
protected String name;
// 准备工作
public abstract void prepare();
// 烘烤
public void bake() {
System.out.println(name + " - 烘烤");
}
public void cut() {
System.out.println(name + " - 切割");
}
public void box() {
System.out.println(name + " - 打包");
}
}
具体产品
/**
* 奶酪披萨 – 具体产品
* @author luxianghai
*
*/
public class BJCheesePizza extends Pizza {
public BJCheesePizza() {
super.name = "北京奶酪披萨";
}
@Override
public void prepare() {
System.out.println(super.name);
System.out.println("准备奶酪中...");
}
}
/**
* 水果披萨 – 具体产品
* @author luxianghai
*
*/
public class BJFruitPizza extends Pizza {
public BJFruitPizza() {
super.name = "北京水果披萨";
}
@Override
public void prepare() {
System.out.println(super.name);
System.out.println("准备水果中...");
}
}
/**
* 奶酪披萨 – 具体产品
* @author luxianghai
*
*/
public class LDCheesePizza extends Pizza {
public LDCheesePizza() {
super.name = "伦敦奶酪披萨";
}
@Override
public void prepare() {
System.out.println(super.name);
System.out.println("准备奶酪中...");
}
}
/**
* 水果披萨 – 具体产品
* @author luxianghai
*
*/
public class LDFruitPizza extends Pizza {
public LDFruitPizza() {
super.name = "伦敦水果披萨";
}
@Override
public void prepare() {
System.out.println(super.name);
System.out.println("准备水果中...");
}
}
抽象工厂
import java.util.Scanner;
import d3_factory.t2_factorymethod.pizza.*;
/**
* 抽象工厂
* @author luxianghai
*
*/
public abstract class PizzaStore {
// 工厂子类需要实现的方法 - 常用创建具体产品
public abstract Pizza createPizza(String type);
public PizzaStore() {
orderPizza();
}
/**
* 点披萨
*/
private void orderPizza() {
Pizza pizza = null;
String type = ""; // 披萨类型
do {
type = getType();
pizza = createPizza(type);
if ( null != pizza ) {
pizza.bake();
pizza.cut();
pizza.box();
} else {
break;
}
} while(true);
}
/**
* 获取披萨类型
* @return
*/
private String getType() {
System.out.println("输入披萨名:");
Scanner scan = new Scanner(System.in);
return scan.next();
}
}
具体工厂
/**
* 伦敦披萨 - 具体工厂
* @author luxianghai
*
*/
public class LDPizzaStore extends PizzaStore {
@Override
public Pizza createPizza(String type) {
Pizza pizza = null;
if ( "奶酪披萨".equals(type) ) {
pizza = new LDCheesePizza();
} else if ( "水果披萨".equals(type) ) {
pizza = new LDFruitPizza();
}
return pizza;
}
}
/**
* BJ披萨 - 具体工厂
* @author luxianghai
*
*/
public class BJPizzaStore extends PizzaStore {
@Override
public Pizza createPizza(String type) {
Pizza pizza = null;
if ( "奶酪披萨".equals(type) ) {
pizza = new BJCheesePizza();
} else if ( "水果披萨".equals(type) ) {
pizza = new BJFruitPizza();
}
return pizza;
}
}
测试
public class Client {
public static void main(String[] args) {
//new BJPizzaStore();
new LDPizzaStore();
}
}
工厂方法就是对简单工厂在进行一次封装而已,由于多态性使得工厂方法仍然保持了简单工厂的优点。
缺点:仍然违反开闭原则
抽象工厂模式:定义了一个interface用于创建相关或有依赖关系的对象簇,而无需指明具体的类。
抽象工厂模式就是将工厂方法改进了一下,使其能够管理多个抽象产品。
从设计层面看,抽象工厂模式就是对简单工厂模式的改进(或者称为进一步的抽象)。
将工厂抽象成两层,AbsFactory(抽象工厂) 和 具体实现的工厂子类。程序员可以根据创建对象类型使用对应的工厂子类。这样将单个的简单工厂类变成了工厂簇,更利于代码的维护和扩展。
抽象工厂模式的主要角色如下:
我们只需要保留下工厂方法代码中的抽象产品和具体产品即可,其他的代码均删除,添加以下代码:
抽象工厂
/**
* 抽象工厂
* @author luxianghai
*
*/
public interface AbsFactory {
Pizza createPizza(String type);
}
具体工厂
/**
* BJ披萨 - 具体工厂
* @author luxianghai
*
*/
public class BJPizzaStore implements AbsFactory {
@Override
public Pizza createPizza(String type) {
Pizza pizza = null;
if ( "奶酪披萨".equals(type) ) {
pizza = new BJCheesePizza();
} else if ( "水果披萨".equals(type) ) {
pizza = new BJFruitPizza();
}
return pizza;
}
}
/**
* 伦敦披萨 - 具体工厂
* @author luxianghai
*
*/
public class LDPizzaStore implements AbsFactory {
@Override
public Pizza createPizza(String type) {
Pizza pizza = null;
if ( "奶酪披萨".equals(type) ) {
pizza = new LDCheesePizza();
} else if ( "水果披萨".equals(type) ) {
pizza = new LDFruitPizza();
}
return pizza;
}
}
业务类
/**
* 业务类
* @author luxianghai
*
*/
public class PizzaStore {
private AbsFactory factory;
public PizzaStore() {
}
public void setFactory(AbsFactory factory) {
this.factory = factory;
orderPizza();
}
/**
* 点披萨
*/
private void orderPizza() {
Pizza pizza = null;
String type = ""; // 披萨类型
do {
type = getType();
pizza = factory.createPizza(type);
if ( null != pizza ) {
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} else {
break;
}
} while(true);
}
/**
* 获取披萨类型
* @return
*/
private String getType() {
System.out.println("输入披萨名:");
Scanner scan = new Scanner(System.in);
return scan.next();
}
}
测试
public class Client {
public static void main(String[] args) {
PizzaStore store = new PizzaStore();
// store.setFactory(new LDPizzaStore());
store.setFactory(new LDPizzaStore());
}
}
此时的类图如下:
简单工厂+配置文件解除耦合
可以通过工厂模式+配置文件的方式解除工厂对象和产品对象的耦合。在工厂类中加载配置文件中的全类名,并创建对象进行存储,客户端如果需要对象,直接进行获取即可。
第一步:创建配置文件
resources/bean.properties 文件内容
ChickenPizza=d3_factory.t4_config_factory.ChickenPizza
FruitPizza=d3_factory.t4_config_factory.FruitPizza
第二步:创建简单工厂
package d3_factory.t4_config_factory;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Properties;
import java.util.Set;
public class SimpleFactory {
// 加载配置文件 - 获取配置文件中配置的全类名,通过反射技术来创建对象
// 1.定义容器来存储Pizza对象
private static HashMap<String, Pizza> map ;
static {
map = new HashMap<String, Pizza>();
// 1.创建Properties对象
Properties properties = new Properties();
// 2.读取配置文件
InputStream is = SimpleFactory.class.getClassLoader().getResourceAsStream("bean.properties");
try {
// 3.加载配置文件
properties.load(is);
// 获取 properties 中所有的键的 Set 集合
Set<Object> keys = properties.keySet();
for ( Object key: keys ) {
// 获取键对应的值
String className = properties.getProperty((String)key);
// 通过反射技术根据全类名创建对象
Class clazz = Class.forName(className);
Pizza pizza = (Pizza)clazz.newInstance();
map.put((String)key, pizza);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 根据名称获取对象
* @param name
* @return
*/
public static Pizza createPizza(String name) {
return map.get(name);
}
}
我水平有限,错误难免,还望各位加以指正。
本文内容到此结束,感谢您的阅读!!!如果内容对你有帮助的话,记得给我三连丫(点赞、收藏、关注)