第四课 简单工厂模式(Simple Factory)
工厂模式的作用就如他的名字,将大量实现共同接口的产品实例话返回,就像真正的工厂一样。工厂模式可以动态的决定将哪一个类实例化,不必实现知道每次要实例化哪一个类。工厂方式呢,有三种形态:
1. 简单工厂(Simple Factory)模式:又称静态工厂方法模式,是最常见的工厂实现。
2. 工厂方法(Factory Method)模式:又称多态性工厂(Polymorphic Factory)模式,貌似了解的人就不多了。
3. 抽象工厂(Abstract Factory)模式:又称工具箱(Toolkit)模式。常见,知道的人很多,但很多人都是错误的理解。
很多人可能这么说“工厂模式直接基于父子,子类的继承和转换…”这是一个设计师群里的人给我回答的,他还给我“废话”了很多,其实我知道,他根本不了解工厂模式,只是用过简单工厂,以为工厂模式就是简单工厂了。这里我可以说,这个理解是错误的,工厂模式的3种形态都有自己的存在价值。(目前工厂方法我还没体会到,需要继续学习)所以首先大家要端正自己的学习态度,然后好好体会。
这节课介绍简单工厂模式。
简单工厂模式,是不同的工厂方法模式的一个特殊实现。为什么这么说呢,往下看,嘿嘿。
咱们用个最直接的办法,从例子还是看起。(Java与模式的例子)
现在有一个农场公司,专门销售各类水果,暂时定为
l 葡萄(Grape)
l 草莓(Strawberry)
l 苹果(Apple)
这里首先定义了一个水果类(Fruit)里面抽取了水果的三个阶段
u 种植plant()
u 生长grow()
u 收获harvest()
Fruit 接口定义如下
1. public interface Fruit
2. {
3. void grow();
4.
5. void harvest();
6.
7. void plant();
8. }
下面看苹果
1. public class Apple implements Fruit
2. {
3.
4. public void grow()
5. {
6. System.out.println("Apple is growing...");
7. }
8.
9. public void harvest()
10. {
11. System.out.println("Apple has been harvested.");
12. }
13.
14. public void plant()
15. {
16. System.out.println("Apple has been planted.");
17. }
18.
19. public int getTreeAge(){ return treeAge; }
20.
21. public void setTreeAge(int treeAge){ this.treeAge = treeAge; }
22.
23. private int treeAge;
24. }
苹果是多年生植物,这里加了一个TreeAge作为扩展的属性。
接着葡萄
1. public class Grape implements Fruit
2. {
3. public void grow()
4. {
5. System.out.println("Grape is growing...");
6. }
7.
8. public void harvest()
9. {
10. System.out.println("Grape has been harvested.");
11. }
12.
13. public void plant()
14. {
15. System.out.println("Grape has been planted.");
16. }
17.
18. public boolean getSeedless()
19. {
20. return seedless;
21. }
22.
23. public void setSeedless(boolean seedless)
24. {
25. this.seedless = seedless;
26. }
27.
28. private boolean seedless;
29. }
同样由于葡萄分为有子和无子的,扩展了Seedless属性。
最后草莓
1.
2. public class Strawberry implements Fruit
3. {
4.
5. public void grow()
6. {
7. System.out.println("Strawberry is growing...");
8. }
9.
10. public void harvest()
11. {
12. System.out.println("Strawberry has been harvested.");
13. }
14.
15. public void plant()
16. {
17. System.out.println("Strawberry has been planted.");
18. }
19.
20. }
草莓没有扩展属性。
然后园丁登场了。
1. public class FruitGardener
2. {
3. public static Fruit factory(String which) throws BadFruitException
4. {
5. if (which.equalsIgnoreCase("apple"))
6. {
7. return new Apple();
8. }
9. else if (which.equalsIgnoreCase("strawberry"))
10. {
11. return new Strawberry();
12. }
13. else if (which.equalsIgnoreCase("grape"))
14. {
15. return new Grape();
16. }
17. else
18. {
19. throw new BadFruitException("Bad fruit request");
20. }
21. }
22. }
园丁(FruitGardener)会根据客户需求,生产出不同的水果(好像生产用在这里有点牵强哦,算了)。如果接到不合法的要求,则会抛出BadFruitException异常。
1. public class BadFruitException extends Exception
2. {
3. public BadFruitException(String msg)
4. {
5. super(msg);
6. }
7. }
在使用的时候,客户端只需要盗用FruitGardener的factory()即可。如下。
1. try
2. {
3. FruitGardener.factory("grape");
4. FruitGardener.factory("apple");
5. FruitGardener.factory("strawberry");
6. ...
7. }
8. catch(BadFruitException e)
9. {
10. do...
11. }
示例代码到此结束。
现在咱们来回顾一下。首先园丁类FruitGardener就是简单工厂类的实现了。
然后分析下,首先有3种水果,这就是我们这个工厂需要生产的对象了。那么示例怎么处理的呢。它首先定义了 Fruit水果的一个接口。将水果共通的特性抽取出来。然后分别给三种水果实现了对应的类对象。最后,在通过园丁类(工厂类)的Factory(“水果名”)这一静态方法,返回类对象。上述几点构成了简单工厂模式的实现。
很简单吧。但是你注意到这里面的关键点了吗?
首先,模式的核心是工厂类,这个类通过必要的逻辑判断(其实一般就是标志了),决定什么时候创建哪一个产品的实例,并返回产品接口引用。从而实现了用接口引用多个实例对象,达到 对产品的可扩展性。如果新增一个产品,只需要新实现一个产品,并修改工厂方法,返回新产品即可。对于产品而言,简单工厂实现了开闭原则,这是优点。但是新增产品后,必然导致了工厂方法的修改,这是他存在的缺点。
在产品种类很相对稳定,取得产品逻辑相对简单的情况下,简单工厂无疑是一个最佳选择。他方便实现,简化取得产品的逻辑,并且有着良好的扩展性。
但是要想进一步提高代码的可扩展性,进一步遵循开闭原则,则需要使用工厂方法模式。下一节我们就来讨论他。
简单工厂简单吧~
有人反映没体现出好处来,追加一个例子,出自《大话设计模式》
目的:要实现一个计算器通用工具类。
根据可变性封装原则,要把这里的计算逻辑进行封装。根据依赖倒转原则,要依靠抽象不要依靠实现。经过分析,不管什么计算,都需要2个计算数和一个获得结果的方法,所以抽象出来了一个计算类Operation.(四则运算也可以分解为各个简单运算的)。
下面给出了常见的各个算法的逻辑实现类(加减乘除开方等等),这里不介绍了。然后通过一个简单工厂方法,根据传入的运算符号,返回相应的运算逻辑类实例。这样做有什么好处了呢。首先自然是开闭原则。假设我加法运算需要修改,我只需要修改加法运算类GetResult()的实现,不需要修改基类方法,并不影响调用运算类的代码。这就达到了开闭原则的对修改关闭,对扩展开放。但是简单工厂的缺点还是存在的,如果新增计算逻辑对象的话,必须要修改工厂类,这就决定了应用简单工厂时,应当基本可以确定逻辑不会变化。例如本例中,我实际应用中可能就只会涉及到这些计算逻辑。所以采用简单工厂。
貌似我又说了很多废话,大家可以自己实现一个计算类,来比较一下。总之还是慢慢体会。
调用方法:
作者:王文斌
转载请注明出处