Java 版 (精华区)
发信人: Javor (猪头小队长·乐呵呵), 信区: Java
标 题: [合集] 简单工厂(Simple Factory)模式
发信站: 哈工大紫丁香 (Mon Jun 7 13:09:05 2004), 站内
────────────────────────────────────────
Javor (猪头小队长·乐呵呵) 于 (Fri May 28 10:55:05 2004) 说道:
//以下是我摘抄编纂《Java设计模式》的第3章得到的。
简单工厂(Simple Factory)模式:
一个简单工厂模式根据所提供的数据返回某个类的一个实例,此类可能
是多个可能的类中的一个。通常它返回的类具有公共的父类和共同的方
法,但每个类执行的任务各不相同,并且针对不同类型的数据进行了优
化。
上面的话可能不是很好懂,没关系,看完下面的例子再翻过来看,也许
就好懂了一些。
例:有一个姓名登记表,要求用户输入姓名,用户可能的输入方式有两
种:一种是:“名 姓”,另一种是“姓,名”。这样我们可以通过判断
字符串中间是否有逗号来判断姓和名的顺序。
那么写一个类来处理名和姓的分离。
public class Namer{
protected String last; //姓
protected String first; //名
public String getFirst(){ //返回姓
return first;
}
public String getLast(){ //返回名
return last;
}
}
针对上面的两种不同情况,分别写两个类:
第一个类:采用空格分隔
public class FirstFirst extends Namer {
//当采用空格分隔姓名时
//由姓中抽取出名
public FirstFirst(String s) {
int i = s.lastIndexOf(" "); //查找分隔空格
if (i>0) {
first = s.substring(0, i).trim();
last =s.substring(i+1).trim();
} else {
first = ""; // 如果不存在空格
last = s; // 则全部存为姓
}
}
}
第二个类:采用逗号分隔
public class LastFirst extends Namer {
//当采用逗号分隔姓名时
//由姓中抽取出名
public LastFirst( String s) {
int i = s.indexOf(","); //查找分隔逗号
if (i > 0) {
last = s.substring( 0, i).trim();
first = s.substring( i + 1).trim();
}
else {
last = s; // 如果不存在逗号
first = ""; // 则全部存为姓
}
}
}
接下来就一个工厂类:
Public class NameFactory {
//根据是否存在逗号
//由工厂决定返回哪一个类。
public static Namer getNamer( String entry) {
int i = entry.indexOf(","); // 逗号决定姓名顺序
if (i > 0)
return new LastFirst( entry );
else
return new FirstFirst( entry );
}
}
这样,只要调用NameFactory.getNamer(String entry),就可以得到一
个适合的Namer来处理。如果以后要增加一个以全角的空格或者其它符号
分隔的Namer,只要多写一个Class和修改一下NameFactory就可以了。
琢磨完这个例子,返回顶部,看看那个综述是否明白了一些呢?
~oh,原来抄书也这么累哦,明白一件事情和说明白一件事情的差别太大
了!:(
────────────────────────────────────────
dongbin (浪花) 于 (Fri May 28 11:08:25 2004) 说道:
Factory模式是我最早使用的模式,因为其使用范围广,实现简单。
使用Factory模式的时候每次增加一种创建方式都要修改Factory的源代码,并且重新编译
,这是最不爽的地方,比较好的的解决方案是使用元数据编程,把各个分支语句中的内容
以及条件语句写到配置文件中,结合reflection,可以做到随着对象种类的增多以及创建
方式改变不会波及到Factory,增加了灵活性和扩展性。Hibernate的*.hbm.xml就是一个好
例子。
关于元数据编程的内容,请参见《程序员修炼之道》
【 在 Javor (猪头小队长·乐呵呵) 的大作中提到: 】
: 这样,只要调用NameFactory.getNamer(String entry),就可以得到一
: 个适合的Namer来处理。如果以后要增加一个以全角的空格或者其它符号
: 分隔的Namer,只要多写一个Class和修改一下NameFactory就可以
: 了。 : ...................
────────────────────────────────────────
Javor (猪头小队长·乐呵呵) 于 (Fri May 28 12:19:34 2004) 说道:
//摘自《Java与模式》
简单工厂模式的优缺点
在简单工厂模式中,一个工厂类处于对产品类实例化的中心位置上,它知
道每一个产品,它决定哪一个产品类应当被实例化。这个模式的优点是允
许客户端相对独立于产品创建的过程,并且在系统引入新产品的时候无需
修改客户端,也就是说,它在某种程度上支持“开-闭”原则。
这个模式的缺点是对“开-闭”原则的支持不够,因为如果有新的产品加
入到系统中去,就需要修改工厂类,将必要的逻辑加入到工厂类中。
【 在 Javor (猪头小队长·乐呵呵) 的大作中提到: 】
: //以下是我摘抄编纂《Java设计模式》的第3章得到的。
: 简单工厂(Simple Factory)模式:
: 一个简单工厂模式根据所提供的数据返回某个类的一个实例,此类可能
: ...................
────────────────────────────────────────
kingheap (A Million Dollars) 于 (Fri May 28 12:25:15 2004) 说道:
转载thinking in pattens
Factories: encapsulating object creation
When you discover that you need to add new types to a system, the most sensible first step is to use polymorphism to create a common interface to those new types. This separates the rest of the code in your system from the knowledge of the specific t
that you are adding. New types may be added without disturbing existing code ... or so it seems. At first it would appear that the only place you need to change the code in such a design is the place where you inherit a new type, but this is not quit
e. You must still create an object of your new type, and at the point of creation you must specify the exact constructor to use. Thus, if the code that creates objects is distributed throughout your application, you have the same problem when adding
ypes—you must still chase down all the points of your code where type matters. It happens to be the creation of the type that matters in this case rather than the use of the type (which is taken care of by polymorphism), but the effect is the same:
g a new type can cause problems.
The solution is to force the creation of objects to occur through a common factory rather than to allow the creational code to be spread throughout your system. If all the code in your program must go through this factory whenever it needs to create
f your objects, then all you must do when you add a new object is to modify the factory.
Since every object-oriented program creates objects, and since it’s very likely you will extend your program by adding new types, I suspect that factories may be the most universally useful kinds of design patterns.
Simple Factory method
As an example, let’s revisit the Shape system. One approach is to make the factory a static method of the base class:
//: c05:shapefact1:ShapeFactory1.java
// A simple static factory method.
package c05.shapefact1;
import java.util.*;
import com.bruceeckel.test.*;
abstract class Shape {
public abstract void draw();
public abstract void erase();
public static Shape factory(String type) {
if(type.equals("Circle")) return new Circle();
if(type.equals("Square")) return new Square();
throw new RuntimeException(
"Bad shape creation: " + type);
}
}
class Circle extends Shape {
Circle() {} // Friendly constructor
public void draw() {
System.out.println("Circle.draw");
}
public void erase() {
System.out.println("Circle.erase");
}
}
class Square extends Shape {
Square() {} // Friendly constructor
public void draw() {
System.out.println("Square.draw");
}
public void erase() {
System.out.println("Square.erase");
}
}
public class ShapeFactory1 extends UnitTest {
String shlist[] = { "Circle", "Square",
"Square", "Circle", "Circle", "Square" };
List shapes = new ArrayList();
public void test() {
for(int i = 0; i < shlist.length; i++)
shapes.add(Shape.factory(shlist[i]));
Iterator i = shapes.iterator();
while(i.hasNext()) {
Shape s = (Shape)i.next();
s.draw();
s.erase();
}
}
public static void main(String args[]) {
new ShapeFactory1().test();
}
} ///:~
The factory( ) takes an argument that allows it to determine what type of Shape to create; it happens to be a String in this case but it could be any set of data. The factory( ) is now the only other code in the system that needs to be changed when a
type of Shape is added (the initialization data for the objects will presumably come from somewhere outside the system, and not be a hard-coded array as in the above example).
To encourage creation to only happen in the factory( ), the constructors for the specific types of Shape are made “friendly,” so factory( ) has access to the constructors but they are not available outside the package.
Polymorphic factories
The static factory( ) method in the previous example forces all the creation operations to be focused in one spot, so that’s the only place you need to change the code. This is certainly a reasonable solution, as it throws a box around the process o
ating objects. However, the Design Patterns book emphasizes that the reason for the Factory Method pattern is so that different types of factories can be subclassed from the basic factory (the above design is mentioned as a special case). However, th
k does not provide an example, but instead just repeats the example used for the Abstract Factory (you’ll see an example of this in the next section). Here is ShapeFactory1.java modified so the factory methods are in a separate class as virtual func
. Notice also that the specific Shape classes are dynamically loaded on demand:
//: c05:shapefact2:ShapeFactory2.java
// Polymorphic factory methods.
package c05.shapefact2;
import java.util.*;
import com.bruceeckel.test.*;
interface Shape {
void draw();
void erase();
}
abstract class ShapeFactory {
protected abstract Shape create();
private static Map factories = new HashMap();
public static void
addFactory(String id, ShapeFactory f) {
factories.put(id, f);
}
// A Template Method:
public static final
Shape createShape(String id) {
if(!factories.containsKey(id)) {
try {
// Load dynamically
Class.forName("c05.shapefact2." + id);
} catch(ClassNotFoundException e) {
throw new RuntimeException(
"Bad shape creation: " + id);
}
// See if it was put in:
if(!factories.containsKey(id))
throw new RuntimeException(
"Bad shape creation: " + id);
}
return
((ShapeFactory)factories.get(id)).create();
}
}
class Circle implements Shape {
private Circle() {}
public void draw() {
System.out.println("Circle.draw");
}
public void erase() {
System.out.println("Circle.erase");
}
private static class Factory
extends ShapeFactory {
protected Shape create() {
return new Circle();
}
}
static {
ShapeFactory.addFactory(
"Circle", new Factory());
}
}
class Square implements Shape {
private Square() {}
public void draw() {
System.out.println("Square.draw");
}
public void erase() {
System.out.println("Square.erase");
}
private static class Factory
extends ShapeFactory {
protected Shape create() {
return new Square();
}
}
static {
ShapeFactory.addFactory(
"Square", new Factory());
}
}
public class ShapeFactory2 extends UnitTest {
String shlist[] = { "Circle", "Square",
"Square", "Circle", "Circle", "Square" };
List shapes = new ArrayList();
public void test() {
// This just makes sure it will complete
// without throwing an exception.
for(int i = 0; i < shlist.length; i++)
shapes.add(
ShapeFactory.createShape(shlist[i]));
Iterator i = shapes.iterator();
while(i.hasNext()) {
Shape s = (Shape)i.next();
s.draw();
s.erase();
}
}
public static void main(String args[]) {
new ShapeFactory2().test();
}
} ///:~
Now the factory method appears in its own class, ShapeFactory, as the create( ) method. This is a protected method which means it cannot be called directly, but it can be overridden. The subclasses of Shape must each create their own subclasses of Sh
ctory and override the create( ) method to create an object of their own type. The actual creation of shapes is performed by calling ShapeFactory.createShape( ), which is a static method that uses the Map in ShapeFactory to find the appropriate facto
ject based on an identifier that you pass it. The factory is immediately used to create the shape object, but you could imagine a more complex problem where the appropriate factory object is returned and then used by the caller to create an object in
re sophisticated way. However, it seems that much of the time you don’t need the intricacies of the polymorphic factory method, and a single static method in the base class (as shown in ShapeFactory1.java) will work fine.
Notice that the ShapeFactory must be initialized by loading its Map with factory objects, which takes place in the static initialization clause of each of the Shape implementations. So to add a new type to this design you must inherit the type, creat
actory, and add the static initialization clause to load the Map. This extra complexity again suggests the use of a static factory method if you don’t need to create individual factory objects.
Abstract factories
The Abstract Factory pattern looks like the factory objects we’ve seen previously, with not one but several factory methods. Each of the factory methods creates a different kind of object. The idea is that at the point of creation of the factory obj
you decide how all the objects created by that factory will be used. The example given in Design Patterns implements portability across various graphical user interfaces (GUIs): you create a factory object appropriate to the GUI that you’re working
and from then on when you ask it for a menu, button, slider, etc. it will automatically create the appropriate version of that item for the GUI. Thus you’re able to isolate, in one place, the effect of changing from one GUI to another.
As another example suppose you are creating a general-purpose gaming environment and you want to be able to support different types of games. Here’s how it might look using an abstract factory:
//: c05:Games.java
// An example of the Abstract Factory pattern.
import com.bruceeckel.test.*;
interface Obstacle {
void action();
}
interface Player {
void interactWith(Obstacle o);
}
class Kitty implements Player {
public void interactWith(Obstacle ob) {
System.out.print("Kitty has encountered a ");
ob.action();
}
}
class KungFuGuy implements Player {
public void interactWith(Obstacle ob) {
System.out.print("KungFuGuy now battles a ");
ob.action();
}
}
class Puzzle implements Obstacle {
public void action() {
System.out.println("Puzzle");
}
}
class NastyWeapon implements Obstacle {
public void action() {
System.out.println("NastyWeapon");
}
}
// The Abstract Factory:
interface GameElementFactory {
Player makePlayer();
Obstacle makeObstacle();
}
// Concrete factories:
class KittiesAndPuzzles
implements GameElementFactory {
public Player makePlayer() {
return new Kitty();
}
public Obstacle makeObstacle() {
return new Puzzle();
}
}
class KillAndDismember
implements GameElementFactory {
public Player makePlayer() {
return new KungFuGuy();
}
public Obstacle makeObstacle() {
return new NastyWeapon();
}
}
class GameEnvironment {
private GameElementFactory gef;
private Player p;
private Obstacle ob;
public GameEnvironment(
GameElementFactory factory) {
gef = factory;
p = factory.makePlayer();
ob = factory.makeObstacle();
}
public void play() { p.interactWith(ob); }
}
public class Games extends UnitTest {
GameElementFactory
kp = new KittiesAndPuzzles(),
kd = new KillAndDismember();
GameEnvironment
g1 = new GameEnvironment(kp),
g2 = new GameEnvironment(kd);
// These just ensure no exceptions are thrown:
public void test1() { g1.play(); }
public void test2() { g2.play(); }
public static void main(String args[]) {
Games g = new Games();
g.test1();
g.test2();
}
} ///:~
In this environment, Player objects interact with Obstacle objects, but there are different types of players and obstacles depending on what kind of game you’re playing. You determine the kind of game by choosing a particular GameElementFactory, and
the GameEnvironment controls the setup and play of the game. In this example, the setup and play is very simple, but those activities (the initial conditions and the state change) can determine much of the game’s outcome. Here, GameEnvironment is n
signed to be inherited, although it could very possibly make sense to do that.
This also contains examples of Double Dispatching and the Factory Method, both of which will be explained later.
【 在 Javor (猪头小队长·乐呵呵) 的大作中提到: 】
: //摘自《Java与模式》
: 简单工厂模式的优缺点
: 在简单工厂模式中,一个工厂类处于对产品类实例化的中心位置上,它知
: ...................
────────────────────────────────────────
dongbin (浪花) 于 (Fri May 28 15:37:41 2004) 说道:
《J2EE设计模式》中的ServiceLocator模式其实就是Simple Factory的延伸。
当工厂创建的对象只需创建一次就可以反复使用时,就可以把产品存储为工厂的属性,从
而避免了多次创建销毁的开销,典型应用就是Ejb的本地接口的远程创建过程,由于开销比
较大,每次查找完成后应该存储起来。
举个例子先
public class ServiceLocator {
HashMap ejbHomeMap;
......
public EjbHome findEjbHome(String ejbName){
EjbHome ret = ejbHomeMap.get(ejbName);
if(ret == null){
ret = createEjbHome(ejbName);
ejbHomeMap.put(ejbName ,ret);
}
return ret;
}
【 在 Javor (猪头小队长·乐呵呵) 的大作中提到: 】
: //以下是我摘抄编纂《Java设计模式》的第3章得到的。
: 简单工厂(Simple Factory)模式:
: 一个简单工厂模式根据所提供的数据返回某个类的一个实例,此类可能
: 是多个可能的类中的一个。通常它返回的类具有公共的父类和共同的方
: 法,但每个类执行的任务各不相同,并且针对不同类型的数据进行了优
: 化。
: 上面的话可能不是很好懂,没关系,看完下面的例子再翻过来看,也许
: 就好懂了一些。
: ...................
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:212.045毫秒