热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

工厂模式和模板方法模式

当创建一组相互之间有关系的类时,在对象创建期间维护他们之间的这些关系是很重要的。要做到这一点,有一个办法是使用工厂方法FactoryMethod设计模式

当创建一组相互之间有关系的类时,在对象创建期间维护他们之间的这些关系是很重要的。要做到这一点,有一个办法是使用工厂方法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章使用这项技术, "状态模式", 基于名称设置状态。

 

总结

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

 

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

 



推荐阅读
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 本文讨论了编写可保护的代码的重要性,包括提高代码的可读性、可调试性和直观性。同时介绍了优化代码的方法,如代码格式化、解释函数和提炼函数等。还提到了一些常见的坏代码味道,如不规范的命名、重复代码、过长的函数和参数列表等。最后,介绍了如何处理数据泥团和进行函数重构,以提高代码质量和可维护性。 ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文介绍了brain的意思、读音、翻译、用法、发音、词组、同反义词等内容,以及脑新东方在线英语词典的相关信息。还包括了brain的词汇搭配、形容词和名词的用法,以及与brain相关的短语和词组。此外,还介绍了与brain相关的医学术语和智囊团等相关内容。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • 3.223.28周学习总结中的贪心作业收获及困惑
    本文是对3.223.28周学习总结中的贪心作业进行总结,作者在解题过程中参考了他人的代码,但前提是要先理解题目并有解题思路。作者分享了自己在贪心作业中的收获,同时提到了一道让他困惑的题目,即input details部分引发的疑惑。 ... [详细]
author-avatar
L-SUNMER
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有