工厂模式和模板方法模式

当创建一组相互之间有关系的类时,在对象创建期间维护他们之间的这些关系是很重要的。要做到这一点,有一个办法是使用工厂方法Factory Method 设计模式。工厂方法模式是一个创建模式,解决了在没有指定具体类型的情况下创建对象的问题。这经常用在抽象类上,专为创建对象定义一个方法。子类就可以覆盖这个方法来定义要创建的特定对象。

工厂方法模式经常与另一个称为模板方法的模式协作,要更好地理解工厂方法并提供更多的上下文含义,我们也将了解模板方法模式。因为工厂方法利用并建立于相同的概念之上,所以我们将首先了解模板方法模式。

抽象类

抽象类在工厂方法和模板方法模式中扮演主要角色。虽然ActionScript 3.0 并不支持抽象类和抽象方法,但是我们依然可以使用抽象类和抽象方法的概念。一个抽象类是这样的一个类,他总是用来被继承,并从不直接实例化。它的用法与接口相似, 但是有一个主要的区别: 一个接口定义只有公有的方法签名,但是一个抽象类的定义既有接口也有执行。一个抽象类使用抽象方法,他没有什么功能,仅仅作为占位符使用。在其他语言中,如C#以及Java, 你可以使用关键字abstract 定义抽象方法,他告诉子类必须覆盖这个方法。因为ActionScript 3.0并没有一个abstract 关键字,所以你可以考虑一个惯用的方法,那就是在抽象方法内部抛出一个异常。这个方式在编译时不会抛出错误,但是在运行时会。底线是ActionScript 3.0没有确定的途径来强制抽象方法。

关于在ActionScript 3.0中使用抽象类,你必须知道两个关键字。第一个是override 关键字。子类必须使用这个关键字来覆盖定义在基类中的抽象方法。他们的方法签名也必须严格匹配。

另一个关键字是final。这个关键字用在抽象类定义的方法上,使得他的子类无法覆盖这个方法。当我们定义模板方法Template Method模式的时候,我们将使用final 关键字。

模板方法Template Method

模板方法是定义在抽象类中的抽象方法,他放置一套普通的算法来填充(至少是部分)抽象方法。算法的定义是在子类覆盖抽象方法的时候完成的。算法的组织结构保存在模板方法中。

考虑下面的例子,我们有一个抽象类,他定义游戏初始化的方式:

package factoryexample {

   public class AbstractGame {

      // 模板方法

      public final function initialize():void {

         createField();

         createTeam("red");

         createTeam("blue");

         startGame();

      }

      public function createField():void {

         throw new Error("抽象方法!");

     }

      public function createTeam(name:String):void {

         throw new Error("抽象方法!");

      }

      public function startGame():void {

         throw new Error("抽象方法!");

      }

   }

}

在上面的例子中,initialize() 方法就是模板方法。他定义了游戏如何被初始化,首先调用createField() 方法,然后调用createTeam()方法创建组,最后调用startGame() 方法开始游戏。他调用的方法在这个类中并没有定义功能。这些是子类的任务,由子类来定义如何创建场地和如何创建组以及游戏如何开始等等。并且所有的子类都只能够按照这种方式来初始化,因为该模板方法是申明为final属性的,不可被覆盖,也就意味着不会被更改。

现在我们将创建一个FootballGame 类,他将扩展我们的AbstractGame 类。这个子类覆盖抽象方法,该抽象方法被抽象基类中的initialize() 模板方法调用。

package factoryexample {

   public class FootballGame extends AbstractGame {

      public override function createField():void {

         trace("创建足球场地");

      }

      public override function createTeam(name:String):void {

         trace("创建足球队: " + name);

      }

      public override function startGame():void {

         trace("开始足球游戏");

      }

   }

}

可以看出, FootballGame 类覆盖了 createField(), createTeam()以及 startGame() 等方法,使他们特定于足球游戏。但是,初始化的算法被保留了。同样的技术也可以用来创建一个BaseballGame 或者BastketballGame 类。我们可以用下面的代码来运行这个例子:

package factoryexample {

   import factoryexample.FootballGame;

   import flash.display.Sprite;

   public class FactoryExample extends Sprite {

      public function FactoryExample() {

         // 创建一个 FootballGame实例

         var game:FootballGame = new FootballGame();

         // 调用定义在 AbstractGame中的模板方法

         game.initialize();

      }

   }

}

下面显示的是输出面板中的内容。

创建足球场地

创建足球队:红队

创建足球队:蓝队

开始足球游戏

你可以看到,子类中覆盖的方法通过模板方法被调用了。算法保留在模板方法中,而具体的执行细节却遵循子类方法。

工厂方法Factory Method

       不用很费神,现在我们可以从前面的模板方法例子转到工厂方法例子了。执行模板方法中的工厂方法是很普通的。

在前面的模板方法例子中,我们的createField() 方法并未返回任何东西,他只是跟踪输出短语 "创建足球场地"。让我们来更新他,使他可以创建并返回一个场地对象。因为不同的游戏有不同的场地类型,所以我们将创建一个接口,名叫IField。所有的场地类都将执行他。我们的接口将只定义一个名叫drawField()的方法:

package factory {

   public interface IField {

      function drawField():void;

   }

}

}

        现在我们将建立一个FootballField 类来执行IField 接口。为了不跑题,我们将不会实际绘制一个足球场在舞台上,但是可以由你自己来完成。这里是FootballField 类的基本定义:

package factoryexample {

   import factoryexample.IField;

   public class FootballField implements IField {

      public function drawField():void {

         trace("正在绘制足球场地");

      }

   }

}

工厂模式Factory Method的目的是连接两个或更多独立的但又有关系的类层次结构。第一个类层次结构是AbstractGame 类和他的子类: FootballGame, BaseballGame以及 BastketballGame。我们的第二个类层次结构是现在的IField接口和执行他的类: FootballField, BaseballField以及 BasketballField。AbstractGame 和IField 对象是有关系的,但是这些特定对象的创建却是由该游戏的子类决定的。图5.1展示这些类层次结构。



 

图 5.1. Factory Method 例子中的类层次结构

 

现在我们可以重新编写AbstractGame类中的createField() 方法和initialize() 方法以反映IField 对象的存在。我们的createField() 方法现在就是一个工厂方法Factory Method,他返回一个执行IField 接口的对象。initialize() 方法现在可以更进一步调用IField 对象的drawField() 方法:

package factoryexample {

   import factoryexample.IField;

   public class AbstractGame {

      //模板方法Template Method,final关键字的使用,使得所有子类的initialize()方法都完全一样(无法进行覆盖),就像“一个模子刻下来的”一样。

      public final function initialize():void {

         var field:IField = createField();

         field.drawField();

         createTeam("red");

         createTeam("blue");

         startGame();

      }

      //工厂方法Factory Method

      public function createField():IField{

         throw new Error("抽象方法!");

      }

      public function createTeam(name:String):void {

         throw new Error("抽象方法!");

      }

      public function startGame():void {

         throw new Error("抽象方法!");

      }

   }

}

        这个抽象类和模板算法还是完全没有特定功能,特定对象的创建还是要靠子类来完成。让我们重新编写FootballGame 类,现在可以让他创建并返回一个FootballField 对象:

package factory {

   import factory.FootballField;

   import factory.IField;

   public class FootballGame extends AbstractGame {

      public override function createField():IField {

         return new FootballField();

      }

      public override function createTeam(name:String):void {

         trace("创建足球队: " + name);

      }

      public override function startGame():void {

         trace("开始足球游戏");

      }

   }

}

如果我们运行这个例子,我们将获得这些输出内容:

正在绘制足球场地

创建足球队:红对

创建足球队:蓝色

开始足球游戏

简单工厂方法Simple Factory

        工厂方法Factory Method经常被误会。 我们经常听到有人说他的代码是按照工厂方法Factory Method模式编写的,仔细审查了这些代码后发现,他们实际上并不是真正的工厂方法Factory Method。过去我们也经常犯这样的错误: 编写一个像下面这样的类,并认为他就是一个工厂方法Factory Method:

package factoryexample {

   public class GameFactory {

      public static function createGame(gameType:String):IGame {

         switch(gameType){

            case "football":

               return new FootballGame();

            case "baseball":

               return new BaseballGame();

            case "basketball":

            default:

               return new BasketballGame();

         }

      }

   }

}

        如果你认为这就是一个工厂方法,那么请你打消这个念头。因为Factory Method 比这个要复杂得多。实际上上面这个例子根本不是一个设计模式。他通常被称为简单工厂Simple Factory 或参数化工厂方法Parameterized Factory。并不是说她就没有用,实际上我们将在第12章使用这项技术, "状态模式", 基于名称设置状态。

总结

        抽象类是面向对象设计中的一个非常重要的工具。他们普遍用在类库和框架方面,因为他们是跨子类提取公共行为的可靠途径。

        在你使用抽象类的时候,模板方法和工厂方法设计模式就是最适合的,也很方便。模板方法允许你创建一个带有普遍性的公共算法,他的特定步骤将由具体的子类来定义。工厂方法允许你触发抽象类中对象的创建,但是将特定类型的对象的创建指向子类。

猜你喜欢

转载自youyu4.iteye.com/blog/2290259