1

Could anybody help me to implement the Decorator design pattern in javascript. I have a TankBase entity:

TankBase = function (x, y, width, height, direction, imageOptions) {
    TankBase.base.call(this, x, y, width, height, imageOptions);
    this.speed = 250;
    this.direction = direction;
    this.render = function (drawEngine) {
        drawEngine.render();
    };
    ...
}

I want to add a new functionality using the Decorator pattern. For example, I want to modify the render() function and draw a health indicator under a tank:

var TankHealthDecorator = function (tank) {
    var _tank = tank;
    this.render = function (drawEngine) {
        // draw a health indicator
        ...
        _tank.render(drawEngine);
    };
}

Usage:

var tank = new TankHealthDecorator(new HeavyTank());

where HeavyTank inherits TankBase.

How should I modify TankHealthDecorator() to use it like a wrapper for a tank instance?

EDIT:

Thank you, Paul, for a great article:

I would start here: addyosmani.com/blog/decorator-pattern Good write up. – Paul

4

4 回答 4

1

从功能上讲,我认为你所拥有的非常接近。我会存储原始渲染函数,分配一个新函数,然后简单地从装饰的函数中应用它。我也认为不需要将装饰器创建为对象,但这可能更像是一种偏好。

var DrawEngine = { render: function() {
    console.log('render');
} };

var TankBase = function (x, y, width, height, direction, imageOptions) {
    this.speed = 250;
    this.direction = direction;
    this.render = function (drawEngine) {
        drawEngine.render();
    };
};

var HeavyTank = function() {
    TankBase.apply(this, arguments);
    this.render = function() {
        console.log('heavyTank Render');
    }
}

function DecorateTankWithHealthIndicator (tank) {
    var oRender = tank.render;
    tank.render = function (drawEngine) {
        console.log('draw a health indicator');
        oRender.apply(tank, arguments);
    };
};

var btank = new TankBase();
var htank = new HeavyTank();
btank.render(DrawEngine);
htank.render(DrawEngine);
DecorateTankWithHealthIndicator(btank);
DecorateTankWithHealthIndicator(htank);
btank.render(DrawEngine);
htank.render(DrawEngine);
于 2013-07-21T08:46:31.597 回答
0

装饰器模式

这是装饰器模式的粗略实现——又名:包装器

背景:

装饰器模式通常用于通过将请求转发到其接受的组件来动态地向对象添加额外的职责,和|或将深层继承层次结构解耦为类型组合(不要与组合模式混淆,后者在层次结构中是组合的-- 使用Component-Leaf结构来允许跨统一的分形拓扑的同义请求 [平等对待父母和孩子])。

function AbstractComponent(){
    this.draw = function(){
        console.log('@AbstractComponent | #draw');
    };
}

function AbstractDecorator(){
    AbstractComponent.call(this);  // Inherit AbstractComponent Interface
    this.notify = function(){
        console.log('@AbstractDecorator | #notify');
    };
}

function ConcreteComponent(options){
    AbstractComponent.call(this);  // Inherit AbstractComponent Interface
    this.fill = function(){
        console.log('@ConcreteComponent | #fill');
    };
}

function ConcreteDecorator(Component){
    AbstractDecorator.call(this);  // Inherit AbstractDecorator Interface
    function PrivateResponsibility(){
        console.log('@ConcreteDecorator | #PrivateResponsibility');
    }
    this.additionalResponsibility = function(){
        console.log('@ConcreteDecorator | #additionalResponsibility');
    };
    this.draw = function(){
        console.log('@ConcreteDecorator | #draw-Component.draw');
        // ... additional logic
        PrivateResponsibility();
        Component.draw();
    };
    this.fill = function(){
        console.log('@ConcreteDecorator | #fill-Component.fill');
        Component.fill();
    };
}

var concreteComponent = new ConcreteComponent();
concreteComponent = new ConcreteDecorator(concreteComponent);  // use same variable name as to allow client code to remain the same

//CLIENT CODE
concreteComponent.draw();
concreteComponent.fill();
concreteComponent.notify();
concreteComponent.additionalResponsibility();
于 2014-09-02T22:19:33.150 回答
0

在下面的方法中,一个 tankBase 对象被传递给 tankHealth 函数。tankBase 对象在 tankHealth 函数中得到修改,将 tankBase 对象保存在 var 中that。修改后that作为修改后的 tankBase 对象返回。

var TankBase = function () {
        this.render = function () {
            console.log('tankBase')
        };
    }

var TankHealthDecorator = function (tank) {
    var that = tank;

    that.render = function() {
        console.log('tankHealth')
    };

    return that;
}

window.onload = function(){
    var tankBase = new TankBase();
    var testHealth = new TankHealthDecorator(new TankBase());

    tankBase.render();     // tank base
    testHealth.render();   // tank health
};
于 2013-07-20T18:09:30.823 回答
-1

以下是我对函数(对象)装饰的理解

 function api_decorator(some_data){
     return function decorator(func){
        return function new_function(args){
         /* do somethinfg with data before and after func */
         console.log("I'm code before with data: "+some_data);
         func(args);
         console.log("I'm code after");
    }
  }
}

function somefunc(data){
    console.log("Hi, I'm func "+data);
}

somefunc("without decoration")
/* injecting somefunc in decorator api */
somefunc=api_decorator("data needed for api")(somefunc)
/* calling decorated with api somefunc */
somefunc("with decoration")
于 2013-11-11T13:04:41.870 回答