《java与模式》读书笔记之一----简单工厂模式
2011-10-08 19:53
302 查看
简单的说,简单工厂模式就是由一个工厂来决定创建出那一种产品类的的实例。这类产品必须都是有相同的父类或者接口。
所以列,它通常涉及到三个角色:
工厂类:也是这个模式的核心,包含了所有的与对象创建相关的所有商业逻辑。
抽象采品:通常是接口或者抽象类,是工厂类的返回值类型,是工厂类创建出的产品的父类或接口
具体产品:继承或者实现抽象产品的具体产品。
他的特征除了以上三个角色以外,还有一个:工厂方法为static的,至于这点我们到后面来说。
典型的代码如下:
接下来我们就来找找这个模式在Java API中的应用。书中提到了这么一个类:DateFormat.的三个getTimeInstance()方法,那么我们就进去看看源码。
可以很明显的看出,这个三个方法都是调用了get()方法,那我们继续跟踪看一下具体实现:
我们看看重点部分:
首先创建了一个LocaleServiceProviderPool,然后将获取实例的具体逻委托给pool,如果获取不到,则返回一个SimpleDateFormat的实例。
其实这个方法他就是一个很明显的简单工厂方法,只不过是这个抽象类自己同时充当了工厂类的角色。
为什么工厂方法是static? 就以DateFormat为例,首先他是一个抽象类,不能自己实例化。如果工厂方法是不静态方法,那么将无法调用,那么我们假设他不是抽象类,那么我们再试想一下:
首先我们创建了一个DateFormat实例,然后调用他的getTimeInstance()方法,来获取一个他的子类的实例。你会不会觉得很别扭,既然我已经有了如下语句:
DateFormat format = new DateFormat(); 为什么我不直接就写DateFormat format = new SimpleDateFormat()列? 还要通过format.getTimeInstance()多此一举列?
其实这个简单工厂方法对于开--闭原则的支持有限。如果要扩展,必须修改工厂方法。有一点要申明的是:简单工厂模式并不是单例模式,但它是单例模式的基础。与单例模式有着很大程序的区别。在后面的文件中,我们将会陆续介绍到。那么我们来看一个实际的例子(从书上Copy过来):
农场有很多水果:苹果、葡萄、草莓,假设现在有一名园丁看守农场,我们需要什么,直接问园丁要即可,园丁会帮我们会采摘过来。那么分析一下: 在这个案例中: 水果就是抽象采品,而苹果、葡萄则是具体产品,园丁则是扮演工厂角色。那么则会有如下代码:
现在我们开始问园丁要苹果:
从打印结果很明显可以看出,fruit的实例类型是Apple。那么写完这个例子,我们有一点值得反思: 一个农场不可能只有三种水果,那么现在我们种植了更多的水果,如:香蕉、梨、西瓜..... 那么现在我们肯定首先是扩展具体产品,接着是要修改园丁类的工厂方法。 那么显然违背了开---闭原则中的“闭”。而此时,我们的园丁是集万千任务于一身,假设园丁因为某些原因不能工作了,那么怎么找谁要水果去列? 所以这里也体现了简单工厂模式的一个问题: 扩展时,需要修改已有的代码,且工厂类的职能过于集中,若工厂类出现问题,则整个模块将不能正常工作。那么有没有改进办法列?
还是那句话:你永远不会是第一个发现问题的。那么接下来要介绍的工厂方法模式,就针对这个问题,就行了改进了。
所以列,它通常涉及到三个角色:
工厂类:也是这个模式的核心,包含了所有的与对象创建相关的所有商业逻辑。
抽象采品:通常是接口或者抽象类,是工厂类的返回值类型,是工厂类创建出的产品的父类或接口
具体产品:继承或者实现抽象产品的具体产品。
他的特征除了以上三个角色以外,还有一个:工厂方法为static的,至于这点我们到后面来说。
典型的代码如下:
public class Factory{ public static Product create(){ //具体的创建逻 return new ConcreteProduct(); } } public interface Product{} public class ConcreteProduct implements Product{ }
接下来我们就来找找这个模式在Java API中的应用。书中提到了这么一个类:DateFormat.的三个getTimeInstance()方法,那么我们就进去看看源码。
/** * Gets the time formatter with the default formatting style * for the default locale. * @return a time formatter. */ public final static DateFormat getTimeInstance() { return get(DEFAULT, 0, 1, Locale.getDefault()); } /** * Gets the time formatter with the given formatting style * for the default locale. * @param style the given formatting style. For example, * SHORT for "h:mm a" in the US locale. * @return a time formatter. */ public final static DateFormat getTimeInstance(int style) { return get(style, 0, 1, Locale.getDefault()); } /** * Gets the time formatter with the given formatting style * for the given locale. * @param style the given formatting style. For example, * SHORT for "h:mm a" in the US locale. * @param aLocale the given locale. * @return a time formatter. */ public final static DateFormat getTimeInstance(int style, Locale aLocale) { return get(style, 0, 1, aLocale); }
可以很明显的看出,这个三个方法都是调用了get()方法,那我们继续跟踪看一下具体实现:
private static DateFormat get(int timeStyle, int dateStyle, int flags, Locale loc) { if ((flags & 1) != 0) { if (timeStyle < 0 || timeStyle > 3) { throw new IllegalArgumentException("Illegal time style " + timeStyle); } } else { timeStyle = -1; } if ((flags & 2) != 0) { if (dateStyle < 0 || dateStyle > 3) { throw new IllegalArgumentException("Illegal date style " + dateStyle); } } else { dateStyle = -1; } try { // Check whether a provider can provide an implementation that's closer // to the requested locale than what the Java runtime itself can provide. LocaleServiceProviderPool pool = LocaleServiceProviderPool.getPool(DateFormatProvider.class); if (pool.hasProviders()) { DateFormat providersInstance = pool.getLocalizedObject( DateFormatGetter.INSTANCE, loc, timeStyle, dateStyle, flags); if (providersInstance != null) { return providersInstance; } } return new SimpleDateFormat(timeStyle, dateStyle, loc); } catch (MissingResourceException e) { return new SimpleDateFormat("M/d/yy h:mm a"); } }
我们看看重点部分:
首先创建了一个LocaleServiceProviderPool,然后将获取实例的具体逻委托给pool,如果获取不到,则返回一个SimpleDateFormat的实例。
其实这个方法他就是一个很明显的简单工厂方法,只不过是这个抽象类自己同时充当了工厂类的角色。
为什么工厂方法是static? 就以DateFormat为例,首先他是一个抽象类,不能自己实例化。如果工厂方法是不静态方法,那么将无法调用,那么我们假设他不是抽象类,那么我们再试想一下:
首先我们创建了一个DateFormat实例,然后调用他的getTimeInstance()方法,来获取一个他的子类的实例。你会不会觉得很别扭,既然我已经有了如下语句:
DateFormat format = new DateFormat(); 为什么我不直接就写DateFormat format = new SimpleDateFormat()列? 还要通过format.getTimeInstance()多此一举列?
其实这个简单工厂方法对于开--闭原则的支持有限。如果要扩展,必须修改工厂方法。有一点要申明的是:简单工厂模式并不是单例模式,但它是单例模式的基础。与单例模式有着很大程序的区别。在后面的文件中,我们将会陆续介绍到。那么我们来看一个实际的例子(从书上Copy过来):
农场有很多水果:苹果、葡萄、草莓,假设现在有一名园丁看守农场,我们需要什么,直接问园丁要即可,园丁会帮我们会采摘过来。那么分析一下: 在这个案例中: 水果就是抽象采品,而苹果、葡萄则是具体产品,园丁则是扮演工厂角色。那么则会有如下代码:
public interface Fruit { void grow(); void plant(); void harvest(); } package com.pattern.factory.simple; public class Apple implements Fruit{ private int treeAge; @Override public void grow() { log("Apple is growiing...."); } @Override public void harvest() { log("Apple has beean harvested...."); } @Override public void plant() { log("Apple has beean planted...."); } public static void log(String msg){ System.out.println(msg); } public int getTreeAge() { return treeAge; } public void setTreeAge(int treeAge) { this.treeAge = treeAge; } } package com.pattern.factory.simple; public class Grape implements Fruit { private boolean seedless; public boolean isSeedless() { return seedless; } public void setSeedless(boolean seedless) { this.seedless = seedless; } public static void log(String msg){ System.out.println(msg); } @Override public void grow() { log("Grape is growing..."); } @Override public void harvest() { log("Grape has been harvested....."); } @Override public void plant() { log("Grape has been planted........"); } } package com.pattern.factory.simple; public class Strawberry implements Fruit { public static void log(String msg){ System.out.println(msg); } @Override public void grow() { // TODO Auto-generated method stub log("Strawberry is growing...."); } @Override public void harvest() { log("Strawberry has been harvested.........."); } @Override public void plant() { log("Strawberry has been planted..........."); } } package com.pattern.factory.simple; public class FruitGardener { public static Fruit factory(String which) throws BadFruitException{ if(which.equalsIgnoreCase("apple")){ return new Apple(); } if(which.equalsIgnoreCase("strawberry")){ return new Strawberry(); } if(which.equalsIgnoreCase("grape")){ return new Grape(); } throw new BadFruitException("bad fruit Exception "); } }
现在我们开始问园丁要苹果:
package com.pattern.factory.simple; public class TestSimple { /** * @param args * @throws BadFruitException */ public static void main(String[] args) throws BadFruitException { Fruit fruit = FruitGardener.factory("apple"); fruit.grow(); } }
从打印结果很明显可以看出,fruit的实例类型是Apple。那么写完这个例子,我们有一点值得反思: 一个农场不可能只有三种水果,那么现在我们种植了更多的水果,如:香蕉、梨、西瓜..... 那么现在我们肯定首先是扩展具体产品,接着是要修改园丁类的工厂方法。 那么显然违背了开---闭原则中的“闭”。而此时,我们的园丁是集万千任务于一身,假设园丁因为某些原因不能工作了,那么怎么找谁要水果去列? 所以这里也体现了简单工厂模式的一个问题: 扩展时,需要修改已有的代码,且工厂类的职能过于集中,若工厂类出现问题,则整个模块将不能正常工作。那么有没有改进办法列?
还是那句话:你永远不会是第一个发现问题的。那么接下来要介绍的工厂方法模式,就针对这个问题,就行了改进了。
相关文章推荐
- 《java与模式》读书笔记4----里氏代换原则(LSP)和依赖倒转原则(DIP)
- 《JAVA与模式》26天系列—第1天—简单工厂模式
- 《JAVA与模式》之简单工厂模式
- 《JAVA与模式》之简单工厂模式
- 《java与模式》读书笔记5----接口隔离原则(ISP)和合成/聚合复用原则(CARP)
- 《java与模式》读书笔记之五----多例模式
- 《java与模式》读书笔记6----迪米特法则(LoD)
- 《java与模式》读书笔记之六----序列键生成器与单例模式及多例模式(一)
- 《JAVA与模式》之简单工厂模式
- 第一章 代码无错就是优?简单工厂模式(读书笔记)
- 《读书笔记》设计模式——简单工厂模式
- 《Java与模式》读书笔记2(12-21)
- 《java与模式》读书笔记之四----什么是模式
- 《Java与模式》学习笔记之一――简单工厂模式(Simple Factory Pattern)
- 《java与模式》读书笔记之七----序列键生成器与单例模式及多例模式(二)
- 6.java设计模式(读书笔记)简单工厂模式、工厂方法模式、抽象工厂模式
- 【读书笔记】将简单工厂模式翻译成C++了
- 《JAVA与模式》之简单工厂模式
- 《JAVA与模式》之简单工厂模式和抽象工厂模式
- 《大话设计模式》读书笔记--简单工厂模式