[转] 如何应用设计模式设计你的足球引擎(三和四)----Design Football Game(Part III and IV)



原文地址:http://www.codeproject.com/KB/cpp/applyingpatterns2.aspx

作者:An ‘OOP‘ Madhusudanan

译者:赖勇浩(http://blog.csdn.net/lanphaday

解决方案架构师:你的进度怎么样?

愚蠢的开发者:是的,我觉得我学会了如何应用 Observer 模式去解决所有问题

解决方案架构师:一个模式解决所有问题?

愚蠢的开发者:啊,还不够么?

介绍

关于本文

本文是这个系列的第二篇文章,在阅读本文之前,你应该已经读过并且理解这个系列的第一篇,题为

  • 学习如何应用设计模式设计你的足球引擎(第一、二部分)

在第一篇文章中,我们讨论了

  • 什么是模式,如何使用它们
  • 如何支持方案应用模式
  • 如何用观察者模式来解决我们的足球引擎的设计问题

本文是前一篇文章的延续,在这里我们将讨论

  • 第三部分:应用 Strategy 模式解决“球队(Team)”和“球队策略(TeamStrategy)”相关的设计问题
  • 第四部分:应用 Decorator 模式来解决“球员(Player)”相关的设计问题

如果你已经不记得这些设计问题,最好翻回第一篇文章去看看再回来继续。


第三部分

 Strategy 模式

在这一节我们将深入策略模式,然后我们应用这个模式来解决第二个设计问题。像前一篇文章那样,在这里我们再来回顾一下我们的第二个设计问题。

如何你能够记起,我们的第二个设计问题是:

  • 特定的设计问题:在比赛进行时,终端用户能够改变它的球队的策略(如从进攻改为防守)
  • 问题泛化:需要客户端(在这里就是球队)使用的算法(球队策略)能够独立改变

如前文所言,当比赛进行时,我们需要改变球队的策略(如从进攻改为防守)。确切来说就是需要从球队分享球队的策略。

我们已知可以利用策略模式来解决上述设计问题,因为它可使客户端(例如球队)使用的算法(例如球队策略)能够独立改变;那么我们来看看如何利用 Strategy 模式来解决这个设计问题。

理解 Strategy 模式

Strategy 模式非常简单,下面是 Strategy 模式的 UML 图:

Fig - Strategy Pattern

它包括如何几个部分

  • 策略(Strategy)

这是一个算法(策略)的抽象类,所有的具体的算法都派生自它。简单地说,它为所有的具体算法(或具体的策略)提供了一个通用的接口。换言之,如果类 Strategy 有个抽象函数叫 foo(),那么所有具体的策略类都应该重载foo() 函数。

  • 具体的策略(ConcreteStrategy)

这个类是我们真正实现算法的地方,或者说,它是类 Strategy 的具体实现。例如 Sort 是实现算法的策略类,而具体的策略可以是 MergeSort 或 QuickSort 等等。

  • 上下文(Context)

Context可由一个或多个策略类配置,它通常策略接口访问具体策略对象。

应用 Strategy 模式

现在让我们想想如何用 Strategy 模式来解决问题。

Fig - Solving Our Second Design Problem

类 TeamStrategy 包含 Play 函数,AttackStrategy 和 DefendStrategy 是它的具体实现。Team 包含策略,策略可以根据比赛的形势而改变(例如,在领先了若干个进球后从进攻策略改为防守策略)。当用 Team 的PlayGame 函数时,它会调用当前策略的 Play 函数,这一切马上生效,干净利索。

通过策略模式,就可以从类 Team 中分离算法(例如团队的策略)。

Strategy 模式实现

TeamStrategy (Strategy)

下面是类 TeamStrategy 的代码:

‘Strategy: The TeamStrategy class

‘This class provides an abstract interface
‘to implement concrete strategy algorithms

Public MustInherit Class TeamStrategy

‘AlgorithmInterface : This is the interface provided
Public MustOverride Sub Play ()

End Class ‘ END CLASS DEFINITION TeamStrategy

AttackStrategy (ConcreteStrategy)

下面是类 AttackStrategy 的代码,它派生自 TeamStrategy:

‘ConcreteStrategy: The AttackStrategy class

‘This class is a concrete implementation of the
‘strategy class.

Public Class AttackStrategy
Inherits TeamStrategy

    ‘Overrides the Play function.
    ‘Let us play some attacking game

    Public Overrides Sub Play()
        ‘Algorithm to attack
        System.Console.WriteLine(" Playing in attacking mode")
    End Sub

End Class ‘ END CLASS DEFINITION AttackStrategy

DefendStrategy (ConcreteStrategy)

下面是类 DefendStrategy 的代码,它也派生自 TeamStrategy:

‘ConcreteStrategy: The DefendStrategy class

‘This class is a concrete implementation of the
‘strategy class.

Public Class DefendStrategy
Inherits TeamStrategy

    ‘Overrides the Play function.
    ‘Let us go defensive
    Public Overrides Sub Play()
        ‘Algorithm to defend
        System.Console.WriteLine(" Playing in defensive mode")
    End Sub

End Class ‘ END CLASS DEFINITION DefendStrategy

Team (Context)

下面是类 Team 的代码,根据我们的设计,一个球队在同一时间只能有一种策略:

‘Context: The Team class
‘This class encapsulates the algorithm

Public Class Team

    ‘Just a variable to keep the name of team
    Private teamName As String

    ‘A reference to the strategy algorithm to use
    Private strategy As TeamStrategy

    ‘ContextInterface to set the strategy
    Public Sub SetStrategy(ByVal s As TeamStrategy)
        ‘Set the strategy
        strategy = s
    End Sub

    ‘Function to play
    Public Sub PlayGame()
        ‘Print the team‘s name
        System.Console.WriteLine(teamName)
        ‘Play according to the strategy
        strategy.Play()
    End Sub

    ‘Constructor to create this class, by passing the team‘s name
    Public Sub New(ByVal teamName As String)
        ‘Set the team name to use later
        Me.teamName = teamName
    End Sub

End Class ‘ END CLASS DEFINITION Team

组合起来

创建球队,然后设置它们的策略,并开始比赛。下面的代码颇为简单而且有详细的注释。

‘GameEngine class for demonstration
Public Class GameEngine

    Public Shared Sub Main()

        ‘Let us create a team and set its strategy,
        ‘and make the teams play the game

        ‘Create few strategies
        Dim attack As New AttackStrategy()
        Dim defend As New DefendStrategy()

        ‘Create our teams
        Dim france As New Team("France")
        Dim italy As New Team("Italy")

        System.Console.WriteLine("Setting the strategies..")

        ‘Now let us set the strategies
        france.SetStrategy(attack)
        italy.SetStrategy(defend)

        ‘Make the teams start the play
        france.PlayGame()
        italy.PlayGame()

        System.Console.WriteLine()
        System.Console.WriteLine("Changing the strategies..")

        ‘Let us change the strategies
        france.SetStrategy(defend)
        italy.SetStrategy(attack)

        ‘Make them play again
        france.PlayGame()
        italy.PlayGame()

        ‘Wait for a key press
        System.Console.Read()

    End Sub

End Class

运行

程序运行后的输出如下:


第四部分

 Decorator 模式

在这一节我们来讲讲如何应用 Decorator 模式来解决第三个设计问题(如有必要可参考前一篇文章)。这个问题是与球员的运行时职责指派相关的(如前锋、后卫等)。

你可以考虑创建一个球员类,然后基于它派生出类似前锋、中锋和后卫等多个子类。但它并非最好的解决方案,正如我们之前讨论的——一个球员可以在某个时刻是前锋,另一个时刻又变成了中锋。至少,在我们的足球引擎中是这样的,所以这是我们的设计问题。

特定的设计问题:球员拥有额外的职责,如前锋、后卫等,而且可以在运行时切换。

问题泛化:在不使用子类化的情况下,需要能够动态地为对象(在这里是指球员)挂接额外的职责(如前锋、中锋等)。

理解 Decorator 模式

Decorator 模式可以动态地为对象增加职责,是子类化之外的完美之选。下面是 Decorator 模式的 UML 图:

Fig - Decorator Pattern

这个模式由以下部分组成

  • 组件(Component)

类 Component 为组件声明了一个抽象接口,我们能够在这些组件上挂接额外的职责。

  • 具体的组件(ConcreteComponent)

类 ConcreteComponent 是类 Component 的具体实现,它真正定义了一个能够挂接的额外职责。

  • 装饰者(Decorator)

类 Decorator 从类 Component 继承而来,这意味着它继承了组件的所有接口(函数、属性等),而且持有一个从组件类继承下来的对象的引用。其实一个具体的装饰者甚至能够持有其它装饰者的引用(因为类 Decorator 本来就是由类 Component 派生而来)。

  • 具体的装饰者(Concrete Decorator)

这个类是真正为组件挂接职责的地方。

应用 Decorator 模式

现在看看如何应用 Decorator 模式来解决与球员相关的设计问题。

Fig - Solving Our Third Design Problem

可以看到从类 Player 继承两个具体的组件——GoalKeeper 和FieldPlayer,另外还有三个具体的装饰者——Forward、MidFielder和Defender。一个球队有 11 个全场球员和一个守门员(译注:作者写错了,应该是 10个全场球员,后文相同的错误不再指出)。我们的设计问题是需要在运行时指派类似前锋、后卫之类的职责给球员。虽然我们只有 11 全场球员,但可能同时有 11 个前锋和 11 个中锋,因为一个球员可以同时既是前锋又是中锋。通过给球员指派多个角色,和交换它们的角色等,使我们能够规划极佳的比赛策略。

例如可以在比赛的某一时刻通过暂时给一个球员指派 Forward 装饰者,使他可以冲刺和射门。

为具体的组件增加额外的职责,首先可以创建一个具体的组件,然后以引用的形式把它指派给装饰者。比如你可以创建一个全场球员,和一个中锋装饰者,将全场球员指派给中锋装饰者可以为它增加中锋的职责。最后,如果你想,你也可以把同一个球员指派给前锋装饰者。在示例代码中的 GameEngine 模块很好地解释了 Decorator 模式。

下面来看看它的实现,代码都加上了很多注释。

Decorator 模式实现

Player (Component)

类 Player 的实现如下:

‘ Component: The Player class

Public MustInherit Class Player

    ‘Just give a name for this player
    Private myName As String

    ‘The property to get/set the name
    Public Property Name() As String
        Get
            Return myName
        End Get
        Set(ByVal Value As String)
            myName = Value
        End Set
    End Property

    ‘This is the Operation in the component
    ‘and this will be overrided by concrete components
    Public MustOverride Sub PassBall()

End Class ‘ END CLASS DEFINITION Player

FieldPlayer (ConcreteComponent)

下面是类 FieldPlayer 的实现:

‘ ConcreteComponent : Field Player class

‘This is a concrete component. Later, we will add additional responsibilities
‘like Forward, Defender etc to a field player.

Public Class FieldPlayer
Inherits Player

    ‘Operation: Overrides PassBall operation
    Public Overrides Sub PassBall ()
        System.Console.WriteLine(_
          " Fieldplayer ({0}) - passed the ball", _
          MyBase.Name)
    End Sub

    ‘A constructor to accept the name of the player
    Public Sub New(ByVal playerName As String)
        MyBase.Name = playerName
    End Sub

End Class ‘ END CLASS DEFINITION FieldPlayer

GoalKeeper (ConcreteComponent)

下面是类 GoalKeeper 的实现:

‘ ConcreteComponent : GaolKeeper class

‘This is a concrete component.
‘Later, we can add additional responsibilities
‘to this class if required.

Public Class GoalKeeper
Inherits Player

    ‘Operation: Overriding the base class operation
    Public Overrides Sub PassBall ()
    System.Console.WriteLine(" GoalKeeper " & _
      "({0}) - passed the ball", MyBase.Name)
    End Sub

    ‘A constructor to accept the name of the player
    Public Sub New(ByVal playerName As String)
        MyBase.Name = playerName
    End Sub

End Class ‘ END CLASS DEFINITION GoalKeeper

PlayerRole (Decorator)

下面是类 PlayerRole 的实现:

‘Decorator: PlayerRole is the decorator

Public Class PlayerRole
Inherits player

    ‘The reference to the player
    Protected player As player

    ‘Call the base component‘s function
    Public Overrides Sub PassBall()
        player.PassBall()
    End Sub

    ‘This function is used to assign a player to this role
    Public Sub AssignPlayer(ByVal p As player)
        ‘Keep a reference to the player, to whom this
        ‘role is given
        player = p
    End Sub

End Class ‘ END CLASS DEFINITION PlayerRole

Forward (ConcreteDecorator)

下面是类 Forward 的实现:

‘ConcreteDecorator: Forward class is a Concrete implementation
‘of the PlayerRole (Decorator) class

Public Class Forward
Inherits PlayerRole

    ‘Added Behavior: This is a responsibility exclusively for the Forward
    Public Sub ShootGoal()
        System.Console.WriteLine(" Forward ({0}) - " & _
          "Shooted the ball to goalpost", _
          MyBase.player.Name)
    End Sub

End Class ‘ END CLASS DEFINITION Forward

MidFielder (ConcreteDecorator)

下面是类 MidFielder 的实现:

‘ConcreteDecorator: MidFielder class is a Concrete implementation
‘of the PlayerRole (Decorator) class

Public Class MidFielder
Inherits PlayerRole

    ‘AddedBehavior: This is a responsibility exclusively for the Midfielder
    ‘(Don‘t ask me whether only mid filders can dribble the ball - atleast
    ‘it is so in our engine)

    Public Sub Dribble()
        System.Console.WriteLine(_
          " Midfielder ({0}) - dribbled the ball", _
          MyBase.player.Name)
    End Sub

End Class ‘ END CLASS DEFINITION Midfielder

Defender (ConcreteDecorator)

下面是类 Defender 的实现:

‘ConcreteDecorator: Defender class is a Concrete implementation
‘of the PlayerRole (Decorator) class

Public Class Defender
Inherits PlayerRole

    ‘Added Behavior: This is a responsibility exclusively for the Defender
    Public Sub Defend()
        System.Console.WriteLine(_
          " Defender ({0}) - defended the ball", _
          MyBase.player.Name)
    End Sub

End Class ‘ END CLASS DEFINITION Defender

组合起来

‘Let us put it together
Public Class GameEngine

Public Shared Sub Main()

    ‘-- Step 1:
    ‘Create few players (concrete components)

    ‘Create few field Players
    Dim owen As New FieldPlayer("Owen")
    Dim beck As New FieldPlayer("Beckham")

    ‘Create a goal keeper
    Dim khan As New GoalKeeper("Khan")

    ‘-- Step 2:
    ‘Just make them pass the ball
    ‘(during a warm up session ;))

    System.Console.WriteLine()
    System.Console.WriteLine(" > Warm up Session... ")

    owen.PassBall()
    beck.PassBall()
    khan.PassBall()

    ‘-- Step 3: Create and assign the responsibilities
    ‘(when the match starts)

    System.Console.WriteLine()
    System.Console.WriteLine(" > Match is starting.. ")

    ‘Set owen as our first forward
    Dim forward1 As New Forward()
    forward1.AssignPlayer(owen)

    ‘Set Beckham as our midfielder
    Dim midfielder1 As New MidFielder()
    midfielder1.AssignPlayer(beck)

    ‘Now, use these players to do actions
    ‘specific to their roles

    ‘Owen can pass the ball
    forward1.PassBall()
    ‘And owen can shoot as well
    forward1.ShootGoal()

    ‘Beckham can pass ball
    midfielder1.PassBall()
    ‘Beckham can dribble too
    midfielder1.Dribble()

    ‘ [ Arrange the above operations to some meaningfull sequence, like
    ‘ "Beckham dribbled and passed the ball to owen and owen shooted the
    ‘ goal ;) - just for some fun ]"

    ‘-- Step 4: Now, changing responsibilities
    ‘(during a substitution)

    ‘Assume that owen got injured, and we need a new player
    ‘to play as our forward1

    System.Console.WriteLine()
    System.Console.WriteLine(" > OOps, Owen " _
       "got injured. " & _
       "Jerrard replaced Owen.. ")

    ‘Create a new player
    Dim jerrard As New FieldPlayer("Jerrard")

    ‘Ask Jerrard to play in position of owen
    forward1.AssignPlayer(jerrard)
    forward1.ShootGoal()

    ‘-- Step 5: Adding multiple responsibilities
    ‘(When a player need to handle multiple roles)

    ‘We already have Beckham as our midfielder.
    ‘Let us ask him to play as an additional forward

    Dim onemoreForward As New Forward()
    onemoreForward.AssignPlayer(beck)

    System.Console.WriteLine()
    System.Console.WriteLine(" > Beckham has " & _
           "multiple responsibilities.. ")

    ‘Now Beckham can shoot
    onemoreForward.ShootGoal()
    ‘And use his earlier responsibility to dribble too
    midfielder1.Dribble()

    ‘According to our design, you can attach the responsibility of
    ‘a forward to a goal keeper too, but when you actually
    ‘play football, remember that it is dangerous ;)

    ‘Wait for key press
    System.Console.Read()

End Sub

End Class

运行

下面是运行程序的输出

结论

在本文中我们讨论了

  • 策略模式及其实现
  • 装饰模式及其实现

暂时就这么多了。事实上,正是 code project 社区对我前一篇文章的支持才鼓气我的勇气来发表这一篇。谢谢所有人的支持和鼓励!

时间: 2024-10-05 03:09:41

[转] 如何应用设计模式设计你的足球引擎(三和四)----Design Football Game(Part III and IV)的相关文章

[转] 如何应用设计模式设计你的足球引擎(一和二)----Design Football Game(Part I and II)

原文地址: http://www.codeproject.com/KB/architecture/applyingpatterns.aspx 作者:An 'OOP' Madhusudanan 译者:赖勇浩(http://blog.csdn.net/lanphaday ) 译者说:这是一篇非常好的文章,有非常棒的例子,非常棒的文笔,非常棒的代码(VB.net编写的,但你肯定读得懂),如果你还不懂设计模式,那它肯定是最适合你的 DPs 文章之一. 第一部分 解决方案架构师:你可以尝试使用模式 愚蠢的

设计模式.设计原则-单一职责原则

1:什么情况下 会使用到单一职责原则设计模式? 当同一个类中同时出现业务和属性等代码的时候或者当同一个类中要做多样事情的时候,就需要将其抽象出来,做成多种不同的接口,以便后续方便扩展单一职责:原则要求一个接口或者类只有一个原因引起变化,也就是一个接口或者类只有一个职责,它就负责一件事情 单一原则的好处:类的复杂性降低,实现责任清晰明确可读性高,复杂性降低可维护性提高,可读性提高变更引起风险性降低,变更是必不可少的,如果接口修改,只影响对应的实现类,对其他接口没有影响 如上图所示:Perion类这

监听器和 利 用观察者设计模式设计一个程序

一.监听器概念 1.事件源:发生事件的对象. 2.监听器:是一个接口,监听事件源上要发生的动作 3.事件:事件对象一般是作为监听器方法的参数存在的,它封装了发生事件的对象 二.Servlet中提供的监听器(8个) 八个监听器分类: 2.1监听ServletContext.HttpSession.ServletRequest对象的创建和销毁的监听器. ServletContextListener:监听ServletContext对象的创建和销毁. HttpSessionListener:监听Htt

Python设计模式——设计原则

1.单一职责原则:每个类都只有一个职责,修改一个类的理由只有一个 2.开放-封闭远程(OCP):开放是指可拓展性好,封闭是指一旦一个类写好了,就尽量不要修改里面的代码,通过拓展(继承,重写等)来使旧的类满足新的需求,而不是修改一个类里面的代码. 3.依赖倒转原则:高层模块不应该依赖底层模块,两个都应该依赖抽象:抽象不应该依赖细节,细节应该依赖抽象.底层模块例如很多工具类,例如专门用于管理sql连接的类,管理文件,管理socket连接的类,高层类指具体实现需求的类.高层类和底层类都不应该相互依赖,

Java-单机版的书店管理系统(练习设计模块和思想_系列 四(2) )

说明: 本博客为补全上篇-Java-单机版的书店管理系统(练习设计模块和思想_系列 四(1) )的,所以如果不懂,请先看上一篇. 本系列都是我一步一步学习来的, 所以,可能比较适合初学设计模块的人来学. 现在补全我目前写的所以代码: 公共类: 用户类型枚举:UserTypeEnum类 package cn.hncu.bookStore.common; /** * 功能:用户类型的枚举!<br/> * 定义在公共模块.<br/> * 变量:<br/> * ADMIN(1,

闲话js前端框架(5)——再看自己一年前设计的微型渲染引擎

闲话js前端框架 前端人员=美工+设计+代码+测试 --题记 专题文章: 一.从avalonjs的模板说起 二.庞大的angularjs 三.再也不想碰DOM 四.组件化?有没有后端的事? 五.再看自己一年前设计的微型渲染引擎 六.在浏览器标准上做文章 七.抛开浏览器,构建应用容器 八.为何Flash.银光和Java都在网页端一蹶不振 本文属 西风逍遥游 原创, 转载请注明出处: 西风世界 http://blog.csdn.net/xfxyy_sxfancy 五.再看自己一年前设计的微型渲染引擎

设计模式设计原则

设计原则详解 设计模式存在的根本原因是为了代码复用,增加可维护性. 开闭原则:对扩展开放,对修改关闭 里氏转换原则:子类继承父类,单独掉完全可以运行 依赖倒转原则:引用一个对象,如果这个对象有底层类型,直接引用底层. 接口隔离原则:每一个接口应该是一种角色 合成/聚合复用原则:新的对象应使用一些已有的对象,使之成为新对象的一部分 迪米特原则:一个对象应对其他对象有尽可能少的了解 综述:站在巨人的肩膀上整体HOLD系统架构 设计模式概念解读 1.设计模式概念文字解读 设计模式是一套被繁复使用,思想

设计模式-设计原则

1. 单一职责原则(Single Responsibility Principle,SRP):就一个类而言,应该仅有一个引起它变化的原因. 如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力.这种耦合会导致脆弱的设计,当变化发生时,设计会遭到意想不到的破坏. 判断是否应该分离出类来,就是如果你能够想到多余一个的动机去改变一个类,那么这个类就具有多余一个的职责. 2. 开放-封闭原则(Open-Closed Principle,OCP):

菜菜读设计模式设计模式——设计原则:面向对象

1.面向对象语言(OOP) 面向对象语言最基本的概念就是类与对象,只有拥有这两个概念的语言才是面向对象语言 一般来说面向对象语言拥有四个特征:封装.继承.抽象.多态 但并不是必须具备这四种特性的语言才能成为面向对象语言,比如说 Go 语言,它没有继承的特性,但我们仍认为它是面向对象语言 2.封装.抽象.继承.多态 封装:类通过暴露有限的访问接口,授权外部仅能以类提供的函数来访问内部信息或数据. 实现封装的机制:访问权限控制 (public\protect\default\private)   同