Search code examples
phpdesign-patternslanguage-agnosticfactory-patternstrategy-pattern

Is this a good use case of the Strategy Pattern?


I'm working with an application that needs to receive several videos, and display them on a particular page, currently these videos can only be from YouTube, due to an implementation that does not allow other providers, because the code to get the video data, as a preview image, was placed directly in the View Helper responsible for displaying the video.

I want to change this structure to make it easy to add new providers, such as Vimeo, and I think the Strategy pattern would be the ideal, I would have in my View Helper the method setVideoUrl( string $url ), this method would call the method getProviderStrategy( string $url ) from the class VideoProviderFactory, this factory class would then return, if available, the strategy class, that implements the interface VideoProvider, for the provider of the video url.

What do you think? This is correct? I Need to change something?

Detail: I initially thought about putting a switch to choose the strategy directly into the View Helper, but after reading this question: I Strategy Pattern with no 'switch' statements? I saw that I was wrong, then the class VideoProviderFactory showed up.


Solution

  • That looks like a very good design, with proper separation of responsibilities.

    To give you some more food for thought, consider how the factory will decide which strategy to create. Later when you want to add another strategy, what needs to change? First, you'd create a new VideoProvider, then you'd have to change the factory switch statement (as you've described) and include the selection logic for this new strategy. Now, this is perfectly fine most of the time, but what if you'd like to add the new strategy without changing the factory?

    One way is to have a factory-like interface with a method that decides based on the URL if it should create a specific VideoProvider; let's call it VideoProviderMatcher (pseudo-code):

    interface VideoProviderMatcher {
      bool understands(url)
      VideoProvider create()
    }
    

    Now, this interface knows if it understands a URL and also how to create the VideoProvider that relates to it. When you need to create a new strategy, you implement both the VideoProvider and the related VideoProviderMatcher. As for the factory, it changes to encapsulate a list of VideoProviderMatchers and delegates to the first that understands the given URL, using a chain of responsibility (pseudo-code):

    class VideoProviderFactory {
      List[VideoProviderMatcher] matchers
      void registerMatcher(VideoProviderMatcher matcher) {
        matchers.add(matcher)
      }
      VideoProvider getVideoProviderFor(url) {
        foreach (matcher in matchers) {
          if (matcher.understands(url)) return matcher.create()
        }
      }
    }
    

    Now the only code that needs to change is the code that creates the factory in the first place. Ideally, it has a list of VideoProviderMatchers that it uses to populate the factory and you just add another item to the list.

    Now, is this worth it? I'd say it depends on the complexity of the matching logic, the will to encapsulate the URL matching together with the video provider, the desire to keep the factory stable when new strategies are added, and the rate at which new strategies are added to the solution.