好[your_time_of_day],
今天我一直在学习javascript(es6)中的构图和工厂函数。我理解composition should be preferred over inheritance并且已经同意(至少在javascript中)。然后,我意识到我的情况是我应该使用合成......
有没有办法可以改变我复杂的,继承的结构,所以类是由函数组成而没有荒谬的装饰器数量?我是否错过了一般的构图(我觉得我有)?
我有一个基类AudioPlayer:
class BaseAudioPlayer {
public track;
protected seekBar;
public togglePlay() {
//
}
public seek(time) {
//some seek methods using this.seekBar
}
}
一些玩家类会像这样延伸:
class MainAudioPlayer extends BaseAudioPlayer {
public loadTrack(track) {
//This is horrible
this.track = track;
}
public setSeekBar(seekBar) {
//This is horrible
this.seekBar = seekBar
}
}
请记住我实际上在父类和子类中有更多的方法,并且在一些孩子中有许多方法不在其他孩子中。当然,没有涉及多重继承,但我认为在某些情况下可能会成为多个相同的儿童玩家的可能性(糟糕!)。
我可以使用许多装饰器,如@playable()
@seekable()
等,但后来我发现,最终,mixin的数量会变得很大。我想我也可以以类似的方式使用工厂功能,但看到同样的问题。
完全披露:我正在使用Angular2并且已经大量削减了代码,以便继续讨论使用哪种设计模式,而不是关于特定框架中的实现。
正如@JocelynLecomte评论的那样,我的问题可能不清楚。
MainAudioPlayer(和其他播放器)继承自BaseAudioPlayer,因为所有音频播放器必须具有togglePlay
,seek
和其他一些方法(特定于angular2,因此不包括在内)。
目前,有三个类继承自BaseAudioPlayer:MainAudioPlayer,DetailAudioPlayer和CardAudioPlayer。在未来可能会有更多,每个都有自己的具体方法。
继承用于避免重复,所有玩家都是 BaseAudioPlayers。但是,所有玩家都有 togglePlay
和seek
方法。
我想使用合成,因为我可以看到玩家未来可能没有seek
方法或类似的东西。
似乎使用合成会在所有玩家类中产生大量的锅炉板代码,我想避免这种情况。
@playable
,@seekable
)答案 0 :(得分:2)
我认为如果你想在基类中重用基本方法,你可能想要使用组合而不是继承(即:将BasePlayerComponent定义为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);
}
}
答案 1 :(得分:1)
为OP的给定方案提出最佳拟合组合方法,实际上取决于如何隐藏和访问数据。当然,基础架构应该是基础/子类型(类,继承)和基于函数的mixin的良好组合。
因此,下一个给定示例代码所示的方法是OP在BaseAudioPlayer
,public track
和protected seekBar
内提供的直接结果。更改此类属性的可见性和读写访问权将对所有类和mixin的相应重构方式产生重大影响。
这是我到目前为止所提出的......
function withTrackManagement(stateValue) { // composable fine grained behavioral unit of reuse (mixin/trait).
var
defineProperty = Object.defineProperty;
// writing the protected `track` value.
function loadTrack(track) {
return (stateValue.track = track);
}
// public `loadTrack` method (write access).
defineProperty(this, 'loadTrack', {
value : loadTrack,
enumerable: true
});
}
function withSeekBar(stateValue) { // composable fine grained behavioral unit of reuse (mixin/trait).
var
defineProperty = Object.defineProperty;
// writing the protected `seekBar` value.
function setSeekBar(seekBar) {
return (stateValue.seekBar = seekBar);
}
// public `setSeekBar` method (write access).
defineProperty(this, 'setSeekBar', {
value : setSeekBar,
enumerable: true
});
}
class BaseAudioPlayer { // base type.
constructor(stateValue) {
var
defineProperty = Object.defineProperty;
// reading the protected `track` value.
function getTrack() {
return stateValue.track;
}
function togglePlay() {
//
}
function seek(time) {
// some seek methods using `stateValue.seekBar`
}
// public protected `track` value (read access).
defineProperty(this, 'track', {
get : getTrack,
enumerable: true
});
// public `togglePlay` method.
defineProperty(this, 'togglePlay', {
value : togglePlay,
enumerable: true
});
// public `seek` method.
defineProperty(this, 'seek', {
value : seek,
enumerable: true
});
}
}
class MainAudioPlayer extends BaseAudioPlayer { // composite type ... extended class with mixin/trait composition.
constructor(stateValue) {
stateValue = (
((stateValue != null) && (typeof stateValue == "object") && stateValue)
|| {}
);
super(stateValue);
withTrackManagement.call(this, stateValue);
withSeekBar.call(this, stateValue);
}
}
var mainPlayer = (new MainAudioPlayer);
console.log("mainPlayer : ", mainPlayer);
console.log("mainPlayer.track : ", mainPlayer.track);
console.log("(mainPlayer.track = 'foo bar') : ", (mainPlayer.track = 'foo bar'));
console.log("mainPlayer.track : ", mainPlayer.track);
console.log("mainPlayer.loadTrack('favourit track') : ", mainPlayer.loadTrack('favourit track'));
console.log("mainPlayer.track : ", mainPlayer.track);
console.log("mainPlayer : ", mainPlayer);
class DetailAudioPlayer extends BaseAudioPlayer { // composite type ... extended class with mixin/trait composition.
constructor(stateValue) {
stateValue = (
((stateValue != null) && (typeof stateValue == "object") && stateValue)
|| {}
);
super(stateValue); // - extending/sub-typing.
//withSpecificBehavior.call(this, stateValue); // - composition.
withTrackManagement.call(this, stateValue); //
//
//withOtherBehavior.call(this, stateValue); //
}
}
class CardAudioPlayer extends BaseAudioPlayer { // composite type ... extended class with mixin/trait composition.
constructor(stateValue) {
stateValue = (
((stateValue != null) && (typeof stateValue == "object") && stateValue)
|| {}
);
super(stateValue); // - extending/sub-typing.
//withSpecificBehavior.call(this, stateValue); // - composition.
withSeekBar.call(this, stateValue); //
//
//withOtherBehavior.call(this, stateValue); //
}
}

.as-console-wrapper { max-height: 100%!important; top: 0; }