2

I'm about to use the 'strategy' pattern in my iOS app, but feel like my approach violates the somehow fundamental MVC pattern.

My app is displaying visual "stories", and a Story consists (i.e. has @properties) of one Photo and one or more VisualEvent objects to represent e.g. animated circles or moving arrows on the photo. Each VisualEvent object therefore has a eventType @property, that might be e.g. kEventTypeCircle or kEventTypeArrow. All events have things in common, like a startTime @property, but differ in the way they are being drawn on the StoryPlayerView.

Currently I'm trying to follow the MVC pattern and have a StoryPlayer object (my controller) that knows about both the model objects (like Story and all kinds of visual events) and the view object StoryPlayerView.

To chose the right drawing code for each of the different visual event types, my StoryPlayer is using a switch statement.

@implementation StoryPlayer

// (...)

- (void)showVisualEvent:(VisualEvent *)event onStoryPlayerView:storyPlayerView
{
    switch (event.eventType) {
        case kEventTypeCircle:
            [self showCircleEvent:event onStoryPlayerView:storyPlayerView];
            break;
    case kEventTypeArrow:
            [self showArrowDrawingEvent:event onStoryPlayerView:storyPlayerView];
            break;
    // (...)
}

But switch statements for type checking are bad design, aren't they? According to Uncle Bob they lead to tight coupling and can and should almost always be replaced by polymorphism.

Having read about the "Strategy"-Pattern in Head First Design Patterns, I felt this was a great way to get rid of my switch statement.

So I changed the design like this:

All specialized visual event types are now subclasses of an abstract VisualEvent class that has a showOnStoryPlayerView: method.

@interface VisualEvent : NSObject

- (void)showOnStoryPlayerView:(StoryPlayerView *)storyPlayerView; // abstract

Each and every concrete subclass implements a concrete specialized version of this drawing behavior method.

@implementation CircleVisualEvent

- (void)showOnStoryPlayerView:(StoryPlayerView *)storyPlayerView
{
    [storyPlayerView drawCircleAtPoint:self.position
                 color:self.color
                 lineWidth:self.lineWidth
                radius:self.radius];
}

The StoryPlayer now simply calls the same method on all types of events.

@implementation StoryPlayer

- (void)showVisualEvent:(VisualEvent *)event onStoryPlayerView:storyPlayerView
{
    [event showOnStoryPlayerView:storyPlayerView];
}

The result seems to be great: I got rid of the switch statement, and if I ever have to add new types of VisualEvents in the future, I simply create new subclasses of VisualEvent. And I won't have to change anything in StoryPlayer.

But of cause this approach violates the MVC pattern since now my model has to know about and depend on my view! Now my controller talks to my model and my model talks to the view calling methods on StoryPlayerView like drawCircleAtPoint:color:lineWidth:radius:. But this kind of calls should be controller code not model code, right?? Seems to me like I made things worse.

I'm confused! Am I completely missing the point of the strategy pattern? Is there a better way to get rid of the switch statement without breaking model-view separation?

2

You are trying to merge two levels of functionality into a single class. That is almost guaranteed to cause a mess in your design. Instead, how about this:

Create another set of classes parallel to the Event classes. Each xxVisualEvent will have a corresponding xxVisualEventRenderer that encapsulates its rendering/display logic. These classes know about both the Model and the View and are essentially extensions of the Controller.

Create a Mapper class (basically a factory), which takes in a VisualEvent and creates a VisualEventRenderer based on the type. Yes, this means a type-based switch, but the class has no logic other than to construct Renderer instances.

Then you can call the Render/Draw method on these Renderer classes instead of on the Event classes. You will retain all the advantages of your approach, such as the loose coupling, etc.

  • I followed your advice, it works and feels good :) – Goodsquirrel Jun 10 '14 at 18:56
  • @Goodsquirrel glad it helped! – metacubed Jun 10 '14 at 20:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.