1

I face an issue if to use a decorator or facade pattern to accomplish my needs.

Imagine a client wants to play a video. He can use the interface

public interface IVideoPlayer
{
    // Prepares everything to set up and plays the video
    void Play();
}

The client should not be concerned with the preparation, so the interface's method's summary states that preparation is done by the Play() method.

Preparation may include different things like connecting to the internet, logging in to the service and so on. There are different preparation steps for different systems.

Facade pattern

I could implement IVideoPlayer using the facade pattern as follows

VideoPlayerFacade : IVideoPlayer
{
    ctor(...)

    void Play()
    {
        _internetConnection.Connect();
        _loginService.Login();
        _actualVideoPlayer.Play();
    }
}

What I dislike with this approach:

  • The preparation (connecting, logging in) seems more like an addition to the actual playing algorithm, which leads me more to the decorator pattern. I rather understand the facade pattern to compose some "equal level" steps to achieve a common goal, like CreateCar() delegating to CreateEngine(), CreateCarBody() and so on.
  • I face a naming issue because the interface the type of _actualVideoPlayer implements could also be named IVideoPlayer, because it IS the actual video player, which also guides me to the decorator pattern. I would need two almost identical interfaces (one with and one without preparation) with quite similar names, which I cannot imagine at the moment.

Decorator pattern

IVideoPlayer could be implemented with the actual playing algorithm as follows

VideoPlayer : IVideoPlayer
{
    void Play()
    {
        // do stuff and algorithms
    }
}

The preparation could be done in a decorator as follows

PreparatingVideoPlayer : IVideoPlayer
{
    ctor(...)

    void Play()
    { 
        _internetConnection.Connect();
        _loginService.Login();
        _decoratee.Play();
    }
}

What I dislike with this approach:

  • Actually the VideoPlayer violates the LSP because it does not conform to the interface by not doing all preparation in the Play() method, which rather leads me to the facade pattern. Or doesn't it violate the LSP because it will always be decorated by the PreparatingVideoPlayer when it is injected into the system?

  • What if another client wants to play video without preparation (because the client knows everything is prepared or prepared everything himself)? He wouldn't be able to because letting IVideoPlayer be injected will always result in getting the PreparatingVideoPlayer, which would lead me to the facade pattern again.

Conclusion

So which pattern is more appropriate or are these patterns appropriate at all? Are my concerns valid?

5
  • Why would a video player need to initiate a connection to the network? That should be handled at the OS and hardware level when your code attempts to open a socket connection to a server in order to start streaming a video or logging in. Commented Nov 21, 2018 at 12:27
  • @GregBurghardt The example is simplified. The video should be played on an external device. On the external device we must establish the connection, on the external device we must log in to the video provider and on the external device the video must be chosen and played.
    – Creepin
    Commented Nov 21, 2018 at 12:38
  • 3
    You realize both examples you've provided are exactly the same, save for the variable names, right?
    – Neil
    Commented Nov 21, 2018 at 13:12
  • @Neil Yes, I see the similarities, aside from _actualVideoPlayer implementing a different interface than IVideoPlayer whereas _decoratee is implementing IVideoPlayer making it the decorator pattern. I am not into giving the construct a name at all costs. But I rather would like to know what pattern is more suitable regarding the intents of the patterns concerning this scenario in contrary to the implementation of the patterns. Additionally are my 'dislikes' to these approaches in this scenario correct?
    – Creepin
    Commented Nov 21, 2018 at 13:32
  • Do you need to use this video player on a non external device? Commented Nov 21, 2018 at 16:19

1 Answer 1

5

Decorator pattern

Let's start off nice: I hate your second solution.

IVideoPlayer is an interface; a "contract" that any implementing class does a certain thing. It's a contract on the code side (between caller and implementation), but in a way it's also interpersonal between the programmers working with it - the compiler won't stop me if I implement IVideoPlayer with Play() erasing the hard drive.

The 2 implementations are not fulfilling the same contract, they just have a method with the same name.

The safety an interface is supposed to provide is not given here. Even worse, because of this expectation of such a contract, it makes it more likely that somebody will get it wrong.

Naming things

There are 2 hard thing in computer science, one of them is naming things. Don't underestimate it, a thesaurus is one of the most undervalued programming tools. You should have 2 different names that convey the difference between the 2 implementations.

To give examples, you could rename the "outer" one to VideoSystem: one is the whole system, while the other one is only the player. Or you could rename the inner one VideoRenderer: one is the whole player, while the other is only the part "rendering" the video.

As the outer one seems to be an exposed API, I would rather go with renaming the inner one, because you can easier explain the inner one to your coworkers than the outer one to your clients.

Patterns

Wikipedia says about the Decorator Pattern

What problems can the Decorator design pattern solve?

  • Responsibilities should be added to (and removed from) an object dynamically at run-time.
  • A flexible alternative to subclassing for extending functionality should be provided.

About the Facade Pattern, Wikipedia says

What problems can the Facade design pattern solve?

  • To make a complex subsystem easier to use, a simple interface should be provided for a set of interfaces in the subsystem.
  • The dependencies on a subsystem should be minimized.

Neither of those is your goal here.

You're not working on this because you want to add or remove things dynamically at run-time, or want to extend functionality, or because your subsystem is too difficult to use, or you have too many dependencies on a subsystem.

You are trying to create some functionality in the first place.

Solution

Don't think "which pattern should I apply". Do what works. In the end your code might turn out to be an already established pattern and then when your coworkers ask about the code you can tell them "I solved it with {pattern name}". That's about all the advantage you get from using a pattern.

All that being said, do the first thing.

However, calling that a facade might be confusing, because a lot of people see facades as rather simple classes, just "passing along" methods (e.g. if you also had a method Connect() calling _internetConnection.Connect();). Examples on Wikipedia say otherwise, but they don't seem to be particularly good (e.g. repeatedly calling new in constructors).

You have a VideoPlayer class, and to do its job, it needs other classes - I'd just call that good old composition. Nobody will misunderstand you if you call it composition.

8
  • 1
    I disagree with patterns only being good to tell people "I solved it with {pattern name}". They are useful for understanding why certain compositions of software occur so frequently in just such a way. It provides insights into the strengths and weaknesses of code, the problems being addressed, and sometimes even who and why it was addressed that way. They can facilitate communication of otherwise paragraph long explanations. That being said, I completely agree with the rest of your answer. Patterns are not magic pixie dust. Before you implement one: study it first, again, and then some more
    – Kain0_0
    Commented Nov 22, 2018 at 2:11
  • 1
    "Let's start off nice: I hate your second solution." Boss? Is that you?
    – Neil
    Commented Nov 22, 2018 at 7:43
  • 1
    Thanks for your answer, I think you nailed the problem and I can totally follow your arguments, so I will take your approach with the given naming proposals
    – Creepin
    Commented Nov 22, 2018 at 10:22
  • 1
    @Kain0_0 I agree, that's why I wrote "That's about all the advantage you get". They are helpful when the code is already written, to explain it to somebody. But for writing the code, they do more bad then good, because it reverses the thinking process: You see a good solution for a certain problem and then you start thinking "Hey, I also have this problem" - but you forgot about the "fine print", your problem is only 90% the same and the solution doesn't actually fit well anymore. Which I think is what happened here.
    – R. Schmitz
    Commented Nov 22, 2018 at 10:31
  • 1
    @R.Schmitz, yes I actually did it that way, which is not the best approach which I noticed now.
    – Creepin
    Commented Nov 22, 2018 at 11:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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