目录
1、需求
一个牛奶项目:
- 牛奶有很多种:甜牛奶(SweetMilk)、酸牛奶(SourMilk)等。
- 制作牛奶有:准备鲜牛奶(prepareMilk)、高温杀菌(pasteurize)、打包装(package)。
- 在高温杀菌的步骤可以加入不同的配料制作不同的牛奶。
- 编程完成牛奶订购功能。
2、传统方式解决问题
2.1、类图
2.2、代码
/**
* 客户端
*/
public class Client {
public static void main(String[] args) {
//来一份甜牛奶和两份酸牛奶
String[] types = {"sweet","sour","sour"};
new Order(types);
}
}
/**
* 牛奶抽象类
*/
abstract class Milk{
private String name;
public void prepareMilk(){
System.out.println("准备新鲜的牛奶");
}
/**
* 高温杀菌
*/
public abstract void pasteurize();
public void packaging(){
System.out.println("将"+name+"打包装");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
/**
* 甜牛奶
*/
class SweetMilk extends Milk{
/**
* 高温杀菌
*/
@Override
public void pasteurize() {
System.out.println("高温杀菌,加入糖");
}
}
/**
* 酸牛奶
*/
class SoutMilk extends Milk{
/**
* 高温杀菌
*/
@Override
public void pasteurize() {
System.out.println("高温杀菌,加入果酸");
}
}
/**
* 订单
*/
class Order{
/**
* 构造器
* @param types 多份牛奶的类型
*/
public Order(String...types){
for(String type:types){
Milk milk = null;
if("sweet".equals(type)){
milk = new SweetMilk();
milk.setName("甜牛奶");
}else if("sour".equals(type)){
milk = new SoutMilk();
milk.setName("酸牛奶");
}
//输出牛奶制作过程
System.out.println("---------------"+milk.getName()+"制作过程------------");
milk.prepareMilk();
milk.pasteurize();
milk.packaging();
System.out.println();
}
}
}
运行结果:
2.3、传统方式解决问题分析
优点:比较好理解,简单易操作
缺点:违反了设计模式的ocp原则(即对扩展开放,对修改关闭)
比如我们现在如果要新增一种牛奶:核桃牛奶(WalnutMilk)
- 先要编写一个WalnutMilk类继承Milk并实现其抽象方法
/**
* 核桃奶类
*/
class WalnutMilk extends Milk{
/**
* 高温杀菌
*/
@Override
public void pasteurize() {
System.out.println("高温杀菌,加入核桃粉");
}
}
- 然后要在订单类Order中新增if-else来判断核桃牛奶(违反了ocp原则)
...
else if("walnut".equals(type)){
milk = new WalnutMilk();
milk.setName("核桃奶");
}
...
2.4、改进的思路分析
- 分析:修改代码可以接受,但是如果我们在其他的地方也有创建Milk的代码,就意味着,也需要修改,而创建Milk的代码,往往有多处;
- 思路:把创建Milk对象封装到一个类中,这样我们有新的Milk种类时。只需要修改该类就可以,其他有创建到Milk对象的代码就不需要修改了 --> 简单工厂模式
3、简单工厂模式
3.1、基本介绍
- 简单工厂模式是属于创建型模式,是工厂模式的一种,简单工厂模式是由1+个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式
- 简单工厂模式:定义了一个创建对象的类,由这个类来封装实例化对象的行为(代码)
- 在软件开发中,当我们会用到大量的创建某种、某类或者某批对象时,就会使用到工厂模式
3.2、简单工厂模式解决问题
3.2.1、分析
设计方案:定义一个可以实例化的Milk对象的类,封装创建对象的代码
3.2.2、类图
3.2.3、代码
/**
* 客户端
*/
public class Client {
public static void main(String[] args) {
//来一份甜牛奶和两份酸牛奶
SimpleFactory simpleFactory = new SimpleFactory();
String[] types = {"sweet","sour","sour"};
new Order(simpleFactory,types);
}
}
/**
* 牛奶抽象类
*/
abstract class Milk{
private String name;
public void prepareMilk(){
System.out.println("准备新鲜的牛奶");
}
/**
* 高温杀菌
*/
public abstract void pasteurize();
public void packaging(){
System.out.println("将"+name+"打包装");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
/**
* 甜牛奶
*/
class SweetMilk extends Milk {
/**
* 高温杀菌
*/
@Override
public void pasteurize() {
System.out.println("高温杀菌,加入糖");
}
}
/**
* 酸牛奶
*/
class SoutMilk extends Milk {
/**
* 高温杀菌
*/
@Override
public void pasteurize() {
System.out.println("高温杀菌,加入果酸");
}
}
/**
* 订单
*/
class Order{
private SimpleFactory simpleFactory;
/**
* 构造器
* @param types 多份牛奶的类型
*/
public Order(SimpleFactory simpleFactory,String...types){
this.simpleFactory = simpleFactory;
for(String type:types){
//从工厂中获取牛奶对象
Milk milk = simpleFactory.createMilk(type);
//输出牛奶制作过程
System.out.println("---------------"+milk.getName()+"制作过程------------");
milk.prepareMilk();
milk.pasteurize();
milk.packaging();
System.out.println();
}
}
}
/**
* 工厂
*/
class SimpleFactory{
public Milk createMilk(String type){
Milk milk = null;
if("sweet".equals(type)){
milk = new SweetMilk();
milk.setName("甜牛奶");
}else if("sour".equals(type)){
milk = new SoutMilk();
milk.setName("酸牛奶");
}
return milk;
}
运行结果和上面的一样。
3.2.4、简单工厂实现分析
现在如果要增加一种新的牛奶:核桃牛奶
- 创建核桃牛奶类
- 修改工厂中的逻辑代码if-else即可(不需要修改订单类Order,符合ocp原则)。
4、简单工厂模式优缺点分析
优点:
- 工厂类是整个模式的关键,包含了必要的逻辑判断,根据外界给定的信息,决定究竟应该创建哪个具体类的对象;
- 通过使用工厂类,外界可以从直接创建具体产品对象的尴尬局面摆脱出来,仅仅需要负责“消费”对象就可以了。而不必管这些对象究竟如何创建及如何组织的,明确了各自的职责和权利,有利于整个软件体系结构的优化。
缺点:
- 由于工厂类集中了所有实例的创建逻辑,违反了高内聚责任分配原则,将全部创建逻辑集中到了一个工厂类中;
- 它所能创建的类只能是事先考虑到的,如果需要添加新的类,则就需要改变工厂类了。
- 当系统中的具体产品类不断增多时候,可能会出现要求工厂类根据不同条件创建不同实例的需求,这种对条件的判断和对具体产品类型的判断交错在一起,很难避免模块功能的蔓延,对系统的维护和扩展非常不利;
- 这些缺点在工厂方法模式中得到了一定的克服。
5、使用场景
- 工厂类负责创建的对象比较少;
- 客户只知道传入工厂类的参数,对于如何创建对象(逻辑)不关心;
- 由于简单工厂很容易违反高内聚责任分配原则,因此一般只在很简单的情况下应用。