Search code examples
javascriptinheritancedesign-patternscomposition

Composition/Inheritance/Factory - Best Pattern For This Case


Good [your_time_of_day],

Today I have been learning about composition and factory functions in javascript (es6). I understand that composition should be preferred over inheritance and have come to agree with that (at least in javascript). Then, I realised that I have a situation where I should be using composition...

Question first:

Is there a way I can change my complex, inherited structure so classes are composed of functions without having ridiculous numbers of decorators? Have I missed something about composition in general (I feel like I have)?

Situation

I have a base class AudioPlayer:

class BaseAudioPlayer {
    public track;
    protected seekBar;

    public togglePlay() {
        //
    }

    public seek(time) {
       //some seek methods using this.seekBar
    }
}

And a few player classes would extend from this like so:

class MainAudioPlayer extends BaseAudioPlayer {
    public loadTrack(track) {
        //This is horrible
        this.track = track;
    }

    public setSeekBar(seekBar) {
        //This is horrible
        this.seekBar = seekBar
    }
}

Please bare in mind I actually have a lot more methods in parent and child classes and there are many methods in some children that are not in others. Of course, there is no multiple inheritance involved but I see at some point that might become a possibility with multiple alike child players (bad!).

I could use many decorators like @playable() @seekable() etc. but then I see that, eventually, the number of mixins would become huge. I guess I could also use factory functions in a similar manner but see the same problem.

Full disclosure: I am using Angular2 and have cut back the code a lot to keep any discussion about which design pattern to use and not about an implementation in a specific framework.

Update 1:

As @JocelynLecomte commented, my question may be unclear.

  • The MainAudioPlayer (and other players) inherit from BaseAudioPlayer since all audio players must have togglePlay, seek, and a few other methods (angular2 specific so not included here).

  • Currently, there are three classes that inherit from BaseAudioPlayer: MainAudioPlayer, DetailAudioPlayer and CardAudioPlayer. In the future there may be more and each has there own specific methods.

  • Inheritance was used to avoid duplication and all players are BaseAudioPlayers. However, all players also have togglePlay and seek methods.

  • I'd like to use composition since I could see a possibility of a player that does not have a seek method or something along those lines in the future.

  • It seems to that using composition would lead to a lot of boiler plate code in all player classes and I'd like to avoid this.

    • Could have many decorators (@playable, @seekable)
    • Could use a base player service (as in @amuse 's answer) and have redundant methods.

Solution

  • I think if you want to reuse the base method in base class,you may want to use composition instead of inheritance (ie:define BasePlayerComponent as a property of MainAudioPlayer):

    class MainAudioPlayer{
        constructor(){
        this.basePlayerComponent=new BasePlayerComponent();
        }
        public loadTrack(track) {
            //This is horrible
            this.track = track;
        }
    
        public setSeekBar(seekBar) {
            //This is horrible
            this.seekBar = seekBar
        }
    
        public togglePlay() {
            this.basePlayerComponent.togglePlay();
        }
    
        public seek(time) {
            this.basePlayerComponent.seek(time);
        }
    }