Jump to content

  • Log In with Google      Sign In   
  • Create Account

We're offering banner ads on our site from just $5!

1. Details HERE. 2. GDNet+ Subscriptions HERE. 3. Ad upload HERE.


Strategy pattern.... am I on the right track?


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
13 replies to this topic

#1 Alpha_ProgDes   Crossbones+   -  Reputation: 4692

Like
1Likes
Like

Posted 10 September 2013 - 06:18 PM

So based on the code in this thread, I've implemented a very simple Strategy pattern. Well I think I did. Basically, is it good, bad, completely off the mark?

Strategy code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace DesignPatterns
{
    class Strategy
    {
    }

    public interface IAdd
    {
        int moveXUnits(int XUnits);
    }

    public abstract class Movement : IAdd
    {
        private int startUnit;
        private int stepUnits;
        private int totalSteps;

        public Movement()
        {
            startUnit = 0;
            stepUnits = 1;
            totalSteps = 0;
        }

        public Movement(int initializeUnit, int initializeStep)
        {
            startUnit = initializeUnit;
            stepUnits = initializeStep;
            totalSteps = startUnit;
        }

        public int moveXUnits(int XUnits)
        {
            stepUnits = XUnits;
            totalSteps += stepUnits;
            return totalSteps;
        }

        private int autoMoveXUnits()
        {
            totalSteps += stepUnits;
            return totalSteps;
        }

        public virtual void Step(int XUnits)
        {
            moveXUnits(XUnits);
        }

        public virtual void Step()
        {
            autoMoveXUnits();
        }

        public virtual void displaySteps()
        {
            Console.WriteLine("I have moved " + totalSteps.ToString() + " units.");
        }
    }

    public class Walk : Movement
    {
        public Walk() : base(1,2){}
    }

    public class Skip : Movement
    {
        public Skip() : base(2,3){}
    }

    public class Run : Movement
    {
        public Run() : base(3,5){}
    }

    public class Character
    {
        private Movement movememtAction;

        public Character()
        {
            movememtAction = new Walk();
        }

        public Character(Movement initMovement)
        {
            movememtAction = initMovement;
        }

        public void setMovement(Movement movementToPerform)
        {
            movememtAction = movementToPerform;
        }

        public void Step()
        {
            movememtAction.Step();
        }

        public void Step(int newSteps)
        {
            movememtAction.Step(newSteps);
        }

        public void displaySteps()
        {
            movememtAction.displaySteps();
        }
    }
}

Main.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DesignPatterns
{
    class Program
    {
        static void Main(string[] args)
        {
            Character Mario = new Character(new Run());
            Character Link = new Character(new Walk());
            Character Zelda = new Character(new Skip());

            Mario.Step();
            Mario.displaySteps();
        }
    }
}

Edited by Alpha_ProgDes, 10 September 2013 - 06:19 PM.

Beginner in Game Development? Read here.
 
Super Mario Bros clone tutorial written in XNA 4.0 [MonoGame, ANX, and MonoXNA] by Scott Haley
 
If you have found any of the posts helpful, please show your appreciation by clicking the up arrow on those posts Posted Image
 
Spoiler

Sponsor:

#2 Maple Wan   Members   -  Reputation: 217

Like
2Likes
Like

Posted 10 September 2013 - 07:35 PM

Where is your override keyword? To realize Polymorphism, you have to implement 3 conditions:

1. Contains inheritance.(Interface or base class)

2. Contains override(use abstract/override or virtual/override or realize the interface method)

3. Mutiple base class objects which point to a derived class reference.

 

You should use override keyword in your derived class to replace the behavior of base class. Otherwise you should use new keyword to prevent derived class to replace the method of base class.


Do my best to improve myself, to learn more and more...


#3 AllEightUp   Moderators   -  Reputation: 4267

Like
3Likes
Like

Posted 10 September 2013 - 09:08 PM

Beyond the code in general, I suspect there is a disconnect in the intention of the strategy pattern here as I mentioned in our chat. What are you changing based on the "strategy" here? The only things being changed are two member values, that is simple C++ initialization pattern and not something you use the "strategy" pattern for.

While there is nothing wrong with your implementation after the fixes, there is still a fundamental issue that this is not the intention of the strategy pattern as described by the GOF design patterns book. Given this code, you have implemented a single strategy: "GroundMovement", the changes in values don't change the strategy and are simply initialization values. To make this an actual strategy, you might add "AirMovement" which calculates gravity effect on the movement algorithm, so the actual code for "moveXUnits" changes between the strategies. The intention of the strategies is to insert new code to change the fundamental behavior of certain common calls, not to simply change some values here and there because that is covered in the non-book design pattern of simple C++.

Hopefully this is helpful. I'm not trying to be an ass, but using patterns means using them for the correct reasons, without that proper application you are writing overly complicated code for no good reason but calling it a pattern not intended for this problem. This leads to confusion and poor code in the long run I'm sorry to say..

Edited by AllEightUp, 10 September 2013 - 09:15 PM.


#4 LorenzoGatti   Crossbones+   -  Reputation: 2761

Like
1Likes
Like

Posted 11 September 2013 - 02:38 AM

IAdd is very oddly named, both because "add" is a verb and because moving units has nothing to do with adding.
Produci, consuma, crepa

#5 LorenzoGatti   Crossbones+   -  Reputation: 2761

Like
0Likes
Like

Posted 11 September 2013 - 02:43 AM

Regarding the strategy, movement is clearly far more complex than counting steps. You should think of extracting interchangeable strategies only after you have multiple kinds of units and multiple kinds of movement; as AllEightUp explains, what you have now is only a draft of a couple of simple unit stats.
Produci, consuma, crepa

#6 Cosmic314   Members   -  Reputation: 1260

Like
3Likes
Like

Posted 11 September 2013 - 07:42 AM

The high level view of the strategy pattern is that the interface to a behavior never changes but the behavior itself changes.  

 

A simple example might be a convex hull strategy pattern.  There are probably 10 different ways to solve the convex hull pattern.  Some perform better than others depending on the type of data that's input to the pattern.  If you need to do convex hull solutions frequently and you monitor data input patterns that you are receiving, you might decide that a different algorithm for the convex hull is more appropriate.  Rather than placing a series of 'if-thens' that surround each convex hull algorithm you can simply have your interface point to the best algorithm.

 

I realize the convex hull solution is a fairly simple example.  You don't really need much more than an 'execute' method, although if you could customize sub-steps that have a common interface it would start to get some benefit.

 

Let's take your example.  Maybe a certain class of creatures implement methods to listen(), assess_opponent(Creature &), look(), attack(Creature &), flee(), etc.  You could have a generalized opponent interaction function that does something like:

if( listen() == OPPONENT_DETECTED )
{
   Creature& opponent = get_detected_opponent();
   bool we_do_attack = assess_opponent(opponent);
   if( we_do_attack )
    {
        attack(opponent);
    }
   else
    {
        flee();
    }
}

You could have a strategy pattern that provides interfaces to those methods with a possible default behavior.  You can then provide a way to change the underlying strategies at run time (this is a crucial difference from the template pattern which chooses behavior at compile time).  For example, maybe your creature has super ears that can echo locate creatures, but somewhere in the course of the game it sustains an injury that renders it stone deaf.  Rather than provide a series of 'if-thens' in the above code, you could change your strategy that implements a 'deaf' version of listen().

 

Essentially what this buys you is the ability to have code that has the same overall logic but lets you vary behavior dramatically.  It saves you from having to change the above code constantly despite changes in underlying behavior, which is really one of the major benefits of design patterns.


Edited by Cosmic314, 11 September 2013 - 07:51 AM.


#7 Norman Barrows   Crossbones+   -  Reputation: 2308

Like
0Likes
Like

Posted 11 September 2013 - 01:57 PM

didn't look at the code too closely, and from the comments it wasn't really necessary, but....

 

in plain engish, in the strategy pattern, you call a function, and pass it a flag somehow (explicitly or implicitly  - such as "this"), and it in turn calls some sub-function based on that flag.  an example might be some move method that used object type to in turn call a move_ground_target or move_flying_target method.

 

the idea is that you can use one api call move(this) to call different move() methods based on "this", to implement different "flight models".

 

its a common way to generalize and reduce the number of API calls when designing an API.

 

for example, in my game library, i have drawing down to one call: Zdraw(drawinfo) that works for mesh&texture, static model, animated model, 2d billboard, or 3d billboard   (   hey! i should add sprites! <g>   ).   all it does is a switch on drawinfo.type and calls the appropriate sub-function based on type. its a perfect example of the strategy or policy pattern.  drawinfo.type is the strategy or policy to be used when drawing.


Edited by Norman Barrows, 11 September 2013 - 02:00 PM.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

 

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

 

 


#8 Servant of the Lord   Crossbones+   -  Reputation: 20937

Like
2Likes
Like

Posted 11 September 2013 - 02:05 PM

Is the allocator parameter of C++ standard library containers an example of the strategy pattern? It customizes the logic based off of the allocator passed in, without changing the functionality of the container itself.


It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.
All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.
Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal

[Fly with me on Twitter] [Google+] [My broken website]

[Need web hosting? I personally like A Small Orange]


#9 Cosmic314   Members   -  Reputation: 1260

Like
2Likes
Like

Posted 11 September 2013 - 02:34 PM

Is the allocator parameter of C++ standard library containers an example of the strategy pattern? It customizes the logic based off of the allocator passed in, without changing the functionality of the container itself.

Yeah, I think that's about right.

 

In my listen() example, my creature class might implement it as a giant series of if/then/else statements every single time I call listen, even though the state of the creature rarely changes.  Rather than have a giant sequence of if/then/elses I could instead pay that cost one time.  For example when the creature goes deaf I can change the listen() strategy to the deaf strategy, which still happens to use the same interface.  Now I have tidier code.  If I call listen() a zillion times, I save a bundle of branch instructions on that if/then/else tree.  Pardon my poor syntax below, but it's enough to get the idea across (I hope!)

void creature_class::listen()
{
   this->InterfaceListen_strategy->execute();
   // place this creature's specific code afterwards, if any, that is invariant
}

void creature_class::set_listen_strategy( <variables> )
{
  if( DEAF ) this->IntefaceListenStrategy = DeafListenConcrete;
  if( BUFFED ) this->IntefaceListenStrategy = BuffedListenConcrete;
  // etc.
}


Edited by Cosmic314, 11 September 2013 - 02:48 PM.


#10 LorenzoGatti   Crossbones+   -  Reputation: 2761

Like
0Likes
Like

Posted 12 September 2013 - 02:13 AM

&nbsp;

void creature_class::listen(){&nbsp; &nbsp;this-&gt;InterfaceListen_strategy-&gt;execute();   // place this creature's specific code afterwards, if any, that is invariant}void creature_class::set_listen_strategy( &lt;variables&gt; ){&nbsp; if( DEAF ) this-&gt;IntefaceListenStrategy = DeafListenConcrete;&nbsp; if( BUFFED ) this-&gt;IntefaceListenStrategy = BuffedListenConcrete;&nbsp; // etc.}
Are you sure you want a public set_listen_strategy()? The listening strategy appears to be a very clear cut example of the most private kind of internal state (of class creature_class):

- Nothing "outside" the creature can reasonably have any say in how it listens to noise.
- Changing the strategy at the wrong time might cause errors (e.g. hearing too much or too little), so it should be encapsulated.
- It's a convenience to organize the listening-related code, and the exact same behaviour could be achieved in completely different ways; there's no reason for outside code to depend on such volatile details.
- The events that can cause a change of InterfaceListen_strategy might instead cause a flag to be set, or something else, or no change; the public interface of the creature should match these events (e.g. making the creature hear a deafening noise, already covered by listen() ) rather than exposing a specific way to implement their consequences and constraining creature_class to use it forever because improper dependencies have been established.
Produci, consuma, crepa

#11 Cosmic314   Members   -  Reputation: 1260

Like
0Likes
Like

Posted 12 September 2013 - 09:52 AM

&nbsp;

void creature_class::listen(){&nbsp; &nbsp;this-&gt;InterfaceListen_strategy-&gt;execute();   // place this creature's specific code afterwards, if any, that is invariant}void creature_class::set_listen_strategy( &lt;variables&gt; ){&nbsp; if( DEAF ) this-&gt;IntefaceListenStrategy = DeafListenConcrete;&nbsp; if( BUFFED ) this-&gt;IntefaceListenStrategy = BuffedListenConcrete;&nbsp; // etc.}
Are you sure you want a public set_listen_strategy()? The listening strategy appears to be a very clear cut example of the most private kind of internal state (of class creature_class):

- Nothing "outside" the creature can reasonably have any say in how it listens to noise.
- Changing the strategy at the wrong time might cause errors (e.g. hearing too much or too little), so it should be encapsulated.
- It's a convenience to organize the listening-related code, and the exact same behaviour could be achieved in completely different ways; there's no reason for outside code to depend on such volatile details.
- The events that can cause a change of InterfaceListen_strategy might instead cause a flag to be set, or something else, or no change; the public interface of the creature should match these events (e.g. making the creature hear a deafening noise, already covered by listen() ) rather than exposing a specific way to implement their consequences and constraining creature_class to use it forever because improper dependencies have been established.

 

I understand and can appreciate your points.  The wisdom of when to use a pattern is something that's acquired through experience.  It's hard to make a simple and complete example for something that requires a certain degree of complexity to explain.  In responding to the OP, I can only assume that the choice has been made as to warrant the use of a pattern.  Saying that, I'll certainly adopt code if it offers material benefit.

 

Let me respond to your points the best as I understand them:

 

In my defense, I never provided a full declaration of creature_class::set_listen_strategy.  But your point is taken.  Reference to setting the strategy pattern should be invisible to things outside of the class that uses it.  If I understand your meaning, we should instead supply something like creature_class::is_now_deaf() and then have that function handle whatever state lies beneath whether it be setting a strategy pattern or something else.  That makes sense.

 

I'm not sure what you mean by:  "Nothing "outside" the creature can reasonably have any say in how it listens to noise."

When I first read the point I interpreted it as being in direct conflict with your advice to make set_listen_strategy private.  But I will assume, that's not what you meant.  If you are referring to sending data to the creature class about the external world interacting with it, we could fix it by either providing a parameter that all listen calls use or we can set the appropriate state information through a different method / interface in the creature_class (or its hierarchy) and refer to it indirectly and under the covers.

 

If I change the strategy at the wrong time it will cause issues.  Sure.  I agree.  But it's also true that if I change that state of any other thing before it is ready to be consumed I would also get unexpected behavior.  While this is of concern, I don't see how it is specifically relevant to the strategy pattern.  It seems to be a more general issue that you are describing.

 

You will get no dispute from me about the 'convenience' factor.  There are probably an infinite numbers of ways we could organize the code.  But that's the point of patterns:  flexibility, maintainability, and convenience.  Because the OP is referring specifically to the Strategy pattern I can only assume the source of this strategy comes from a well known source.  A well known source happens to be the classic GoF book.  In it, they specifically give this as a reason to employ the pattern.  Under their 'applicability' section these are the reasons they give:

Use the strategy pattern when

-- many related classes differ only in their behavior.  Strategies provide a way to configure a class with one of many behaviors.

-- you need different variants of an algorithm.  For example, you might define algorithms reflecting different space/time trade-offs.  Strategies can be used when these variants are implemented as a class hierarchy of algorithms

-- an algorithm uses data that clients shouldn't know about.  Use the Strategy pattern to avoid exposing complex, algorithm-specific data structures.

-- a class defines many behaviors, and these appear as multiple conditional statements in its operations.  Instead of many conditionals, move related conditional branches into their own Strategy class.

Moderators:  This is a quote from the GoF book.  I cite fair use but I don't know if this exceeds the bounds you're willing to tolerate.  I will comply with requests to remove if I'm breaking a rule.

 

Back to the discussion.  For the creature class it is this last point for which I've decided that the Strategy pattern happens to be useful.  Later in their description they give an example of a 'switch' ladder and how the Strategy pattern makes the code easier to understand.

 

I'm a little unclear about your last point.

 

I'll add a final thought.  Maybe I have it all wrong and you do offer valid points.  This creature example is somewhat contrived.  I certainly won't be juryrigging Strategy patterns at each and every spot that might avoid 'if-then-else' or 'switch' ladders. After all, there is a cost in factoring code to use any pattern.  If we know that creature_class is something that is very well defined and will not change then it's probably not worth any extra management apparatus, It will only get in the way.

 

I know my response probably appears very defensive.  Please receive them only with attempts at giving an earnest, respectable reply.  smile.png


Edited by Cosmic314, 12 September 2013 - 09:57 AM.


#12 Alpha_ProgDes   Crossbones+   -  Reputation: 4692

Like
0Likes
Like

Posted 12 September 2013 - 01:11 PM


I understand and can appreciate your points. The wisdom of when to use a pattern is something that's acquired through experience. It's hard to make a simple and complete example for something that requires a certain degree of complexity to explain. In responding to the OP, I can only assume that the choice has been made as to warrant the use of a pattern. Saying that, I'll certainly adopt code if it offers material benefit.

 

Actually, the example is arbitrary. I'm not trying to force fit the Strategy pattern in my code (existing or new). I'm actually just wanting to understand the Strategy pattern and see if my example is correct, in the right area, close but no cigar, or just wrong. Of course, I'm looking for feedback or sample code on how to make it better.


Beginner in Game Development? Read here.
 
Super Mario Bros clone tutorial written in XNA 4.0 [MonoGame, ANX, and MonoXNA] by Scott Haley
 
If you have found any of the posts helpful, please show your appreciation by clicking the up arrow on those posts Posted Image
 
Spoiler

#13 Washu   Senior Moderators   -  Reputation: 5416

Like
0Likes
Like

Posted 12 September 2013 - 01:56 PM


Actually, the example is arbitrary. I'm not trying to force fit the Strategy pattern in my code (existing or new). I'm actually just wanting to understand the Strategy pattern and see if my example is correct, in the right area, close but no cigar, or just wrong. Of course, I'm looking for feedback or sample code on how to make it better.

I would say your example is far from the point.

 

With strategy the underlying behavior of each object implementing the strategy is different. With yours the underlying behaviors are all the same (hence why you can default construct them.

 

An OK example is the one used by Wikipedia, which implements a strategy for processing various types of mathematical commands like Add, Subtract, and Multiply. I suggest reading it over.

 

I would also strongly suggest reading the c2 wiki on strategy and state.


In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.
ScapeCode - Blog | SlimDX


#14 Nypyren   Crossbones+   -  Reputation: 4782

Like
0Likes
Like

Posted 12 September 2013 - 06:50 PM

To me, Strategy is just something you get when you implement two or more algorithms that produce the same effect that can be used interchangeably, where all differences are encapsulated.  If the results are different, it's not a Strategy.  Interchangeable movement methods like walking vs. flying do totally different things (the flier can FLY), which violates the definition in my mind.  That's polymorphism.  That's not Strategy.  Boyer-Moore vs. every-character string searching is a Strategy.






Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS