加入收藏 | 设为首页 | 会员中心 | 我要投稿 安卓应用网 (https://www.0791zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 程序设计 > 正文

打破最后的依赖-Head First Design Patterns对工厂的解释

发布时间:2020-05-23 20:10:09 所属栏目:程序设计 来源:互联网
导读:翻译作者:zming 翻译自:http://today.java.net/pub/a/today/2005/04/14/dependency.html 转载请注明出处:http://blog.csdn.net/zmxj/archive/2005/05/25/380784.aspx <<Head First Design Patterns>>一书的Factory 模式章节中,建议我们


翻译作者:zming
翻译自:
http://today.java.net/pub/a/today/2005/04/14/dependency.html
转载请注明出处:http://blog.csdn.net/zmxj/archive/2005/05/25/380784.aspx

<<Head First Design Patterns>>一书的Factory 模式章节中,建议我们要“Breaking the Last Dependency”,即打破最后的依赖,并且展示了如何写出完全远离具体类的代码。下面我们来看看这个主题。
看看breaking the last dependency 是什么意思?它是如何来描述工厂模式的?以及我们为什么应该关注它?所有的工厂模式都是封装具体类的实例并帮助你将代码和具体类的依赖减少到最少。看下面的代码:

public class StampedeSimulator {
Actor actor = null;
public void addActor(String type) {
if (type.equals("buffalo")) {
actor = new Buffalo();
} else if (type.equals("horse")) {
actor = new Horse();
} else if (type.equals("cowboy")) {
actor = new Cowboy();
} else if (type.equals("cowgirl")) {
actor = new Cowgirl();
}
// rest of simulator here
}
}
这段代码中包含了四个不同的具体类(Buffalo,Horse,Cowboy,and Cowgirl),结果他建立了依赖关系在你的代码和这些具体类之间,这为什么是一件坏事呢?你想想,如果你要加入一个新的类型(比如Coyote)或者重新配置具体类(比如你想用FastHorse类替代普通的Horse类),你将重新修改你的代码,这造成难维护性。切记,可能类似的代码会遍布你的所有代码中,如果你要修改这个代码需要到多处修改。注意我们不要寄希望于Java5.0的enumerations匹配字符串来减少这些代码,不是所有的用户都可以在Java5平台下的(比如苹果系统的用户),我们将作其他的实践。

现在我们有没有一个好的方法减少具体类的依赖呢?那将使你的生活更加轻松,减少你大量的代码维护工作,办法就是使用Factory.
有几种类型的工厂,用哪一种你可以查相关的模式书。为了我们的事例,让我们看看Static Factory,它由一个类组成,它提供一个静态方法来操纵一个对象的实例。要实现这个,我们将所有实例代码放到一个factory里,ActorFactory,替换上面StampedeSimulator代码,用factory来创建对象:

public class ActorFactory {
static public Actor createBuffalo() {
return new Buffalo();
}
static public Actor createHorse() {
return new Horse();
}
static public Actor createCowboy() {
return new Cowboy();
}
static public Actor createCowgirl() {
return new Cowgirl();
}
}

And we can alter our StampedeSimulator to look like this:

public class StampedeSimulator {
Actor actor = null;

public void addActor(String type) {
if (type.equals("buffalo")) {
actor = ActorFactory.createBuffalo();
} else if (type.equals("horse")) {
actor = ActorFactory.createHorse();
} else if (type.equals("cowboy")) {
actor = ActorFactory.createCowboy();
} else if (type.equals("cowgirl")) {
actor = ActorFactory.createCowgirl();
}


仅这样只是得到了一点改善,因为代码中还有两个if else then子句。我们还可以进一步改进,我们来参数化工厂,用一个String来标示具体实例的类型:

public class ActorFactory {
static public Actor createActor(String type) {
if (type.equals("buffalo")) {
return new Buffalo();
} else if (type.equals("horse")) {
return new Horse();
} else if (type.equals("cowboy")) {
return new Cowboy();
} else if (type.equals("cowgirl")) {
return new Cowgirl();
} else {
return null;
}
}
}

public class StampedeSimulator {
Actor actor = null;
public void addActor(String type) {
actor = ActorFactory.createActor(type);
// rest of stampede simulator here
}
}

现在我们已经很好分离了具体类和我们的代码中的依赖。注意,工厂中的方法的返回类型是一个接口(Actor)或者也可以是一个抽象类。这使得你的客户端不需要知道具体的类是什么,因而,在你的客户端代码里使用接口,你将继续解耦和你的具体类的依赖。静态工厂创建你需要的对象,你的客户端代码不需要担心它。现在,如果你需要改变代码,你只需要去一个地方,实例都被封装了。

这样把具体类封装到工厂中是很好的事,我们解耦了主要代码和具体类之间的依赖。但是工厂本身仍然依赖于具体的类,如果我们需要改变那些类,就是说需要修改工厂的代码,重新编译,那样不是我们想要做的,我们希望移除所有这样的依赖在我们的代码里。

在我们继续之前,我要指出静态工厂(Static Factory)是一种经常被使用的超过真正的设计模式的惯用方法,但是象这样使用的人常常用单词“工厂(Factory)”来应用这个创建对象的方法. 无论如何,你能使用我们正要结束的静态工厂或者仍何使用真正的工厂模式的技术(like the Factory Method or Abstract Factory patterns).

Let's Break that Last Dependency
(让我们打破最后的依赖)

我们解耦了应用主要代码和具体类的依赖,但是Static Factory,ActorFactory仍然牢牢地绑定着具体的类,加之丑陋的if-then-else语句仍然存在。我们如何才能改善这些移除最后的依赖呢?

有一种技术是使用java的Class.forName()。forName()方法允许你用指定的包路径下的类名动态的装入类。一旦你要取得类,你只需要用实例化一个它的新实例,并且返回它。 让我们看他怎样工作:

class ActorFactory {
static public Actor createActor(String type) {
Actor actor = null;
Class actorClass = null;
try {
actorClass = Class.forName(type);
} catch (ClassNotFoundException e) {
System.out.println("Error: class " + type + " not found.");
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
if (actorClass != null) {
try {
actor = (Actor) actorClass.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
return actor;
}
}

这个代码更加解耦了你的应用和具体类的依赖,因为现在你可以通过传递类名(或至少一些实现了Actor接口的类)给工厂,你就可以取得类的实例。我们为此付出的代价就是我们不得不检测所有可能的途径:首先,确信我们传递的类名字串的类事实存在,并且确信你能够实例化这个类,我们可以在这偷个懒,我们可以在不能装入或实例化一个类而发生异常时,打印出异常的stacktrace,在实际应用中,显然你不得不做的更多。我们也用灵活性换取了少许对静态类型检测的控制。你将要通过稍微的思考,对于实例,它能够完美的合法的为我们装入Actor类,但是我们不能实际上从Actor实例一个对象,因为他是一个接口。

一旦我们修改了ActorFactory,我们需要在你的应用代码里做一些小的修改,我们需要传递由String描述的actor类。像这样:

simulator.addActor("headfirst.factory.simulator.Buffalo");
simulator.addActor("headfirst.factory.simulator.Horse");
simulator.addActor("headfirst.factory.simulator.Cowboy");
simulator.addActor("headfirst.factory.simulator.Cowgirl");


像这样,我们能够编译和运行这个代码并且和先前得到相同的结果:每一个actor类型被实例化了。

现在,当我们想要改变stampede simulator的actors时(例如,我们要拍一个电影,用动画的演员替换真实的演员),所有要做的就是改变我们传递给addActor()方法的描述actor类型的String串即可。我们根本不需要改变ActorFactory or StampedeSimulator中的任何代码。

Taking It All the Way


这是一个改进,但是代码仍然和在actors的指定类型偶合,我们仍然需要指定在代码中和传递给addActor()方法的Actor 类型的名字,意思就是当我们要改变演员的时候不得不重新编译代码,有什么其他的方法取得演员的类型,而没有代码依赖我们想要的演员的类型吗?

有一个办法就是我们删除所有依赖具体类型的代码,指定我们想要的actors的类型在一个properties文件,在运行时装入他们。这样我们就没有依赖具体演员类型的代码了。这样做,我们改变指定的演员类型。替换硬编码actor类型,用编码载入类型从一个叫做actor.properties的properties文件。这个文件每行是你需要的一个演员类型,看起来像这样:

buffalo = headfirst.factory.simulator.Buffalo
horse = headfirst.factory.simulator.Horse
cowboy = headfirst.factory.simulator.Cowboy
cowgirl = headfirst.factory.simulator.Cowgirl



这是一个标准格式的java properties文件:等号两边分别是属性名和属性值。现在可以替换传递给createActor()方法的actor的类型的完整路径名,我们只要传递一个描述类型的串给他(就象我们的第一个版本中代码那样),这个串将对应于properties文件中的属性名:

simulator.addActor("buffalo");
simulator.addActor("horse");
simulator.addActor("cowboy");
simulator.addActor("cowgirl");

(编辑:安卓应用网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读