工厂模式
学习工厂模式必须知道的概念:
- 产品
类 - 抽象产品
抽象类和接口 - 产品簇
多个内在联系或者有逻辑关系的产品
比如三秦套餐中:凉皮+冰峰+肉夹馍 - 产品等级
产品等级和产品簇的关系:
我们平时创建对象的方式:
//抽象产品
interface Foot {
void eat();
}
//具体产品(汉堡包
class Hamburger implements com.shi.design.simple_factory.Foot {
@Override
public void eat() {
System.out.println("在吃汉堡包!");
}
}
//上面代码为服务端,下面为客户端,我们身份为客户身份,不能修改服务端代码
//=============================================
public class Test {
public static void main(String[] args) {
Foot f = new Hamburger();
f.eat();
}
}
上面的这种设计十分脆弱,因为只要服务端修改了代码,客户端也必须跟着修改,这样的设计,服务端和客户端是耦合的。我们需要的是,无论服务端怎么修改,我们客户端的代码是不需要改变的。
简单工厂模式
针对上面的问题,我们采用简单工厂模式进行修改
//抽象产品
interface Foot {
void eat();
}
//具体产品(汉堡包
class Hamburger implements Foot {
@Override
public void eat() {
System.out.println("在吃汉堡包!");
}
}
//具体产品(米线
class RiceNoodle implements Foot {
@Override
public void eat() {
System.out.println("在吃米线!");
}
}
//简单工厂类
class FootFactory {
public static Foot getFoot(int type) {
Foot foot = null;
switch (type) {
case 1:
foot = new Hamburger();
break;
case 2:
foot = new RiceNoodle();
break;
}
return foot;
}
}
//时光线----上面为很久以前的代码,我们不能修改,下面为现在我们书写的代码
//==================================================
//
public class AppTest {
public static void main(String[] args) {
Foot foot = FootFactory.getFoot(1);
foot.eat();
}
}
简单工厂的优点::
- 将具体产品的类型从客户端中解耦出来
- 服务器端如果修改了具体产品的类名,客户端也不知道
- 这复合了面向接口编程的思想
缺点:
- 客户端不得不死记硬背那些常量与具体产品的映射关系
- 如果具体产品特别多,那么简单工厂就会别的特别臃肿,比如如果有一百个具体产品,那么就得有100个case
- 如果变化来了,客户端需要新添加方法,那么就得修改简单工厂中的内容,不符合开闭原则
简单工厂的UML类图
工厂方法模式
针对于简单工厂产生的问题,我们使用工厂方法模式
interface Foot {
void eat();
}
//具体产品(汉堡包
class Hamburger2 implements Foot {
@Override
public void eat() {
System.out.println("在吃汉堡包!");
}
}
//具体产品(米线
class RiceNoodle implements Foot {
@Override
public void eat() {
System.out.println("在吃米线!");
}
}
//设置工厂接口
interface FootFactory {
Foot getFoot();
}
//汉堡包工厂
class HambergerFactory implements FootFactory {
@Override
public Foot getFoot() {
return new Hamburger2();
}
}
//米线接口
class RiceNoodleFactory implements FootFactory {
@Override
public Foot getFoot() {
return new RiceNoodle();
}
}
class Product {
public static void Evaluate(FootFactory footFactory){
Foot foot = footFactory.getFoot();
System.out.println("评委A开始品尝");
foot.eat();
Foot foot1 = footFactory.getFoot();
System.out.println("评委B开始品尝");
foot1.eat();
Foot foot2 = footFactory.getFoot();
System.out.println("评委C开始品尝");
foot2.eat();
}
}
//===================================
//扩充方法
class ColdRice implements Foot {
@Override
public void eat() {
System.out.println("在吃凉皮!");
}
}
//凉皮创建工厂
class ColdRiceFactory implements FootFactory {
@Override
public Foot getFoot() {
return new ColdRice();
}
}
public class AppTest {
public static void main(String[] args) {
HambergerFactory hambergerFactory = new HambergerFactory();
Foot foot = hambergerFactory.getFoot();
foot.eat();
Product.Evaluate(new HambergerFactory());
}
}
优点:
- 仍然具有简单工厂的优点,服务器端修改了具体的产品的类名以后,客户端不知道
- 当客户端需要新添加一个新的产品时,不需要修改作者原来的代码,只需要扩展一个新的工厂
杆点:
- 我们都知道,简单工厂也好,工厂方法也好,都有一个优点,就是服务器端的具体类名修改了以后,客户端不知道!但是,反观我们现在的代码,客户端依然依赖于服务器端具体工厂的类名呀,要是服务器端的具体工厂的类名修改了,那么不是本地的调用的方法内部也得修改?
解释: 工厂的名称,视为接口,作者有责任有义务保证工厂的名称是稳定的,也就是说,虽然客户端依赖于工厂的具体类名,可是在IT行业内,所有工厂的名字都是趋向于稳定的(并不是100%不会变),但是至少工厂类的名称,要比具体类的名字更加稳定。 - 既然产品是我们客户端自己扩展出来的,那么为什么不直接自己实例化呢,毕竟自己扩展出来的ColdRice这个产品,自己能够把控,为什么还要创建工厂呢?
解释: 作者开发功能的时候,不仅仅会开发一些抽象产品,具体产品,对应的工厂,还会套配一些提前做好的架构,比如上面服务端提供的Product的品尝功能。 - 现在只做出ColdRiceFactory,是为了把ColdRiceFactory传入到Product.Evaluate中,所以需要需要创建这个ColdRiceFactory,那么为什么从一开始,就让Product.Evaluate接收的是Food参数呢,而不现在的FoodFactory参数?
解释: 1)、那么在客户端传入的时候就需要传入具体类名,而不是传入工厂,那么如果传入具体类名,那么服务端具体名称修改,则我们也需要修改,2)、该业务品尝中,如果传入Foot的话,那么就是每一个评委都是吃的是同一份食物,而传入FootFactory则每一个评委在吃食物的时候就可以自己new一份食物而不是吃上一个吃剩下的。
缺点: 如果有多个产品等级,那么工厂类的数量就会爆炸式增长。
工厂方法模式的UML类图:
抽象工厂方法
针对工厂方法模式的缺点,我们使用抽象工厂模式。
简单来说,其实抽象工厂与工厂的区别就是将造对象的过程模拟化,比如,套餐,所有店里面都有吃和喝的,所以创建的时候就将吃和喝的放在同一个工厂里面创建,从而减少代码。
//食物接口
interface Foot {
void eat();
}
//具体产品(汉堡包
class Hamburger implements Foot {
@Override
public void eat() {
System.out.println("在吃汉堡包!");
}
}
//具体产品(米线
class RiceNoodle implements Foot {
@Override
public void eat() {
System.out.println("在吃米线!");
}
}
//饮料接口
interface Drink {
public void drink();
}
//抽象产品 可乐
class Cola implements Drink {
@Override
public void drink() {
System.out.println("在喝可乐");
}
}
//抽象产品 冰峰
class IcePeak implements Drink {
@Override
public void drink() {
System.out.println("在喝冰峰");
}
}
//设置工厂接口
interface Factory {
Foot getFoot();
Drink getDrink();
}
//肯德基(KFC)工厂
class KFCFactory implements Factory {
@Override
public Foot getFoot() {
return new Hamburger();
}
@Override
public Drink getDrink() {
return new Cola();
}
}
//三秦工厂
class SanQinFactory implements Factory {
@Override
public Foot getFoot() {
return new RiceNoodle();
}
@Override
public Drink getDrink() {
return new IcePeak();
}
}
class Product {
public static void Evaluate(Factory footFactory){
Foot foot = footFactory.getFoot();
Drink drink = footFactory.getDrink();
System.out.println("评委A开始品尝");
foot.eat();
drink.drink();
Foot foot1 = footFactory.getFoot();
Drink drink1 = footFactory.getDrink();
System.out.println("评委B开始品尝");
foot1.eat();
drink1.drink();
Foot foot2 = footFactory.getFoot();
Drink drink2 = footFactory.getDrink();
System.out.println("评委C开始品尝");
foot2.eat();
drink2.drink();
}
}
//===================================
public class AppTest {
public static void main(String[] args) {
KFCFactory kfcgerFactory = new KFCFactory();
Foot foot = kfcgerFactory.getFoot();
foot.eat();
Product.Evaluate(new KFCFactory());
}
}
优点:
- 任然具有普通工厂和抽象工厂的优点,
- 更重要的是,抽象工厂吧工厂类的数量减少了,无论多少个产品等级工厂就一套。
杆点:
- 为啥三秦工厂的时候就是米粉加冰峰,不可以是米粉加可乐?
解释: 抽象工厂中,可以生产多个产品, 这多个产品之间,必须都存内在联系。
缺点:
- 当产品等级发生变化时(添加商品等级,删除商品等级的时候),都会引起所有工厂代码的修改,就违反了开闭原则。
抽象工厂UML类图
结论:当产品等级比较固定,就可以考虑使用抽象工厂,如果产品等级经常变化,则不建议使用抽象工厂。
以上内容为课后总结,如有侵权,请告知本人!