JavaScript设计模式介绍

由于JavaScript不是典型的面向对象语言,因而在实现一些经典的设计模式上也与一般语言存在差异,本文主要介绍在JavaScript中如何实现常用的设计模式。

1. 单例模式

单例模式是最常见的设计模式,在一般的OOP语言中,我们可以通过私有化构造函数实现单例模式。但由于单例模式实际上可以看做返回的是一个结构,该结构在内存中有且仅有唯一的一份,所以可以类比JavaScript中的闭包,所以可以记住闭包完成单例模式的实现:

// 单例模式
var mySingleton = (function(){
    var instance;

    init = function() {
        var privateVar = "privateVar";
        privateFunc = function() {
            console.log("This is private func");
        };
        return {
            publicVar: 'public var', // 公共变量
            publicFunc: function() { // 公共方法
                console.log('This is public func');
            },
            getPrivateVar: function() {
                return privateVar;
            }
        }
    };

    return {
        getInstance: function() {
            if (!instance) {
                instance = init();
            }
            return instance;
        }
    }

})();

var singleton1 = mySingleton.getInstance();
var singleton2 = mySingleton.getInstance();
singleton1.publicFunc();
console.log(singleton1 === singleton2);

2. 观察者模式

观察者模式下存在两个成员:观察者和被观察者。观察者在被被观察者处进行注册,当被观察者相关状态发生变化时,被观察者告知观察者,同时观察者执行相应更新逻辑。通常来说,存在多个观察者观察同一个被观察者的情况。在观察者模式下,存在以下几个组件:

  • 被观察者:维护一组被观察接口,用于添加、删除观察者,通知观察者
  • 观察者:维护一组观察者接口,用于在被观察者状态发生变化时,通知到观察者
  • 具体的被观察者:实现被观察者接口
  • 具体的观察者:实现观察者接口
// 观察者模式:建立观察者/被观察者关系,观察者可以注册其观察对象(被观察者),当被观察者的状态发生改变时,可以及时通知到观察者

// 被观察者管理观察者能力建模
function ObserverList() {
    this.observerList = [];
}

// 添加观察者
ObserverList.prototype.Add = function(observer) {
    this.observerList.push(observer);
}

// 清空观察者
ObserverList.prototype.Empty = function() {
    this.observerList = [];
}

// 观察者数量
ObserverList.prototype.Count = function() {
    return this.observerList.length;
}

// 获取某个观察者
ObserverList.prototype.Get = function(index) {
    if (index >= 0 && index < this.observerList.length) {
        return this.observerList[index];
    }
    return undefined;
}

// 删除某个观察者
ObserverList.prototype.RemoveAt = function( index ){
    if( index === 0 ){
      this.observerList.shift();
    }else if( index === this.observerList.length -1 ){
      this.observerList.pop();
    }
};

// var testObserverList = new ObserverList();
// for(var key in testObserverList) {
//     console.log('key:' + key + '->' + testObserverList[key]);
// }

// 给某个对象扩展被观察者能力
function extend(extension, target) {
    for(var key in extension) {
        target[key] = extension[key];
    }
}

// 创建被观察者对象Subject,同时集成观察者对象的能力
function Subject() {
    this.observerList = new ObserverList();
};

Subject.prototype.AddObserver = function(observer) {
    this.observerList.Add(observer)
};

Subject.prototype.RemoveObserver = function( observer ){
    this.observers.RemoveAt( this.observers.IndexOf( observer, 0 ) );
}; 

// 通知所有观察者
Subject.prototype.Notify = function(context) {
    var count = this.observerList.Count();
    for(var i = 0; i < count; i++) {
        this.observerList.Get(i).Update(context);
    }
};

// 构建观察者对象,主要是定义观察后的处理函数
function Observer() {
    this.Update = function() {
        //do something
    }
}

接下来我们基于观察者模式实现一个例子:

  • 一个按钮,这个按钮用于增加新的充当观察者的选择框到页面上
  • 一个控制器的选择框,充当一个被观察者,通知其他选择框是否应该被选中
  • 一个容器,用于放置新的选择框
    <body>
        <button id="addNewObserver">Add New Observer checkbox</button>
        <input id="mainCheckbox" type="checkbox"/>
        <div id="observersContainer"></div>
    </body>
<script src="./observer.js"></script> <!-- 引入上文中的js代码 -->    

<script type="text/javascript">
        var controlCheckbox = document.getElementById('mainCheckbox');
        var addBtn = document.getElementById('addNewObserver');
        var container = document.getElementById('observersContainer');

        // 给controlCheckbox扩展被观察者能力
        extend(new Subject(), controlCheckbox);

        controlCheckbox.addEventListener('click', function() {
            this.Notify(this.checked);
        });

        // 添加观察者
        addBtn.addEventListener('click', AddNewObserver);
        function AddNewObserver() {
            // 创建一个checkbox
            var check = document.createElement('input');
            check.type = 'checkbox';
            check.checked = controlCheckbox.checked;

            // 扩展观察者能力
            extend(new Observer(), check);
            check.Update = function(checked) {
                this.checked = checked;
            } 

            //添加到controlCheckbox的观察者列表中
            controlCheckbox.AddObserver(check);

            // 添加到容器区域
            container.appendChild(check);
        }
}

3 订阅模式

订阅模式和观察者模式很类似,都是建立观察者与被观察者之间的消息通道。观察者模式需要观察者显示的调用被观察者的观察接口来声明观察关系,从而在代码层面存在依赖关系。而订阅模式通过使用主题/事件频道将订阅者和发布者进行解耦。

// 订阅者对象
function Subscriber() {
    this.subscriberEventList = [];
}

Subscriber.prototype.addSubscribe = function(subscribe) {
    this.subscriberEventList.push(subscribe);
}

// 订阅事件对象
function Subscribe(name, callback) {
    this.name = name;
    this.callback = callback;
}

// 发布事件对象
function Publish(name, context) {
    this.name = name;
    this.context = context;
}

//订阅中心对象
function SubscribeCenter() {
    this.subscriberList = [];
}

SubscribeCenter.prototype.addSubscriber = function(subscriber) {
    this.subscriberList.push(subscriber);
}

SubscribeCenter.prototype.publish = function(publisher) {
    var name = publisher.name;
    var context = publisher.context;
    for(var i = 0; i < this.subscriberList.length; i++) {
        for(var j = 0; j < this.subscriberList[i].subscriberEventList.length; j++) {
            var subscribeevent = this.subscriberList[i].subscriberEventList[j];
            if(subscribeevent.name === name) {
                subscribeevent.callback.call(this.subscriberList[i], name, context);
            }
        }
    }
}

function extend(extend, obj) {
    for(var key in extend) {
        obj[key] = extend[key];
    }
}

4. 工厂模式

工厂模式的实质由一个工厂类来代理对象(工厂模式下称为组件)的构造,组件遵循同一套组件接口,使用方只需按照工厂定制的标准将参数传递给工厂类的组件构造函数即可。工厂模式实现了组件使用方与组件之间的解耦,使得两者之间不存在显示的依赖关系,特别适合于组件众多的情况。

// 工厂模式
// A constructor for defining new cars
function Car( options ) {

      // some defaults
      this.doors = options.doors || 4;
      this.state = options.state || "brand new";
      this.color = options.color || "silver";

    }

    // A constructor for defining new trucks
    function Truck( options){

      this.state = options.state || "used";
      this.wheelSize = options.wheelSize || "large";
      this.color = options.color || "blue";
    }

    // FactoryExample.js

    // Define a skeleton vehicle factory
    function VehicleFactory() {}

    // Define the prototypes and utilities for this factory

    // Our default vehicleClass is Car
    VehicleFactory.prototype.vehicleClass = Car;

    // Our Factory method for creating new Vehicle instances
    VehicleFactory.prototype.createVehicle = function ( options ) {

      if( options.vehicleType === "car" ){
        this.vehicleClass = Car;
      }else{
        this.vehicleClass = Truck;
      }

      return new this.vehicleClass( options );

    };

    // Create an instance of our factory that makes cars
    var carFactory = new VehicleFactory();
    var car = carFactory.createVehicle( {
                vehicleType: "car",
                color: "yellow",
                doors: 6 } );

    // Test to confirm our car was created using the vehicleClass/prototype Car

    // Outputs: true
    console.log( car instanceof Car );

    // Outputs: Car object of color "yellow", doors: 6 in a "brand new" state
    console.log( car );

    // 抽象工厂
    var AbstractVehicleFactory = (function () {

            // Storage for our vehicle types
            var types = {};

            return {
                getVehicle: function ( type, customizations ) {
                    var Vehicle = types[type];

                    return (Vehicle ? new Vehicle(customizations) : null);
                },

                registerVehicle: function ( type, Vehicle ) {
                    var proto = Vehicle.prototype;

                    // only register classes that fulfill the vehicle contract
                    if ( proto.drive && proto.breakDown ) {
                        types[type] = Vehicle;
                    }

                    return AbstractVehicleFactory;
                }
            };
        })();

        // Usage:

        AbstractVehicleFactory.registerVehicle( "car", Car );
        AbstractVehicleFactory.registerVehicle( "truck", Truck );

        // Instantiate a new car based on the abstract vehicle type
        var car = AbstractVehicleFactory.getVehicle( "car" , {
                    color: "lime green",
                    state: "like new" } );

        // Instantiate a new truck in a similar manner
        var truck = AbstractVehicleFactory.getVehicle( "truck" , {
                    wheelSize: "medium",
                    color: "neon yellow" } );

5. Mixin模式

mixin是javascript中最为常用的一种模式,几乎所有javascript框架都用到了mixin。既可以将任意一个对象的全部和部分属性拷贝到另一个对象或类上。Mix允许对象以最小量的复杂性从外部借用(或者说继承)功能.作为一种利用Javascript对象原型工作得很好的模式,它为我们提供了从不止一个Mix处分享功能的相当灵活,但比多继承有效得多得多的方式。

// Define a simple Car constructor
var Car = function ( settings ) {

            this.model = settings.model || "no model provided";
            this.color = settings.color || "no colour provided";

        };

    // Mixin
    var Mixin = function () {};

    Mixin.prototype = {

        driveForward: function () {
            console.log( "drive forward" );
        },

        driveBackward: function () {
            console.log( "drive backward" );
        },

        driveSideways: function () {
            console.log( "drive sideways" );
        }

    };

    // Extend an existing object with a method from another
    function augment( receivingClass, givingClass ) {

        // only provide certain methods
        if ( arguments[2] ) {
            for ( var i = 2, len = arguments.length; i < len; i++ ) {
                receivingClass.prototype[arguments[i]] = givingClass.prototype[arguments[i]];
            }
        }
        // provide all methods
        else {
            for ( var methodName in givingClass.prototype ) {

                // check to make sure the receiving class doesn't
                // have a method of the same name as the one currently
                // being processed
                if ( !Object.hasOwnProperty(receivingClass.prototype, methodName) ) {
                    receivingClass.prototype[methodName] = givingClass.prototype[methodName];
                }

                // Alternatively:
                // if ( !receivingClass.prototype[methodName] ) {
                //  receivingClass.prototype[methodName] = givingClass.prototype[methodName];
                // }
            }
        }
    }

    // Augment the Car constructor to include "driveForward" and "driveBackward"
    augment( Car, Mixin, "driveForward", "driveBackward" );

    // Create a new Car
    var myCar = new Car({
        model: "Ford Escort",
        color: "blue"
    });

    // Test to make sure we now have access to the methods
    myCar.driveForward();
    myCar.driveBackward();

    // Outputs:
    // drive forward
    // drive backward

    // We can also augment Car to include all functions from our mixin
    // by not explicitly listing a selection of them
    augment( Car, Mixin );

    var mySportsCar = new Car({
        model: "Porsche",
        color: "red"
    });

    mySportsCar.driveSideways();

    // Outputs:
    // drive sideways

6. 装饰模式

装饰模式动态地给一个对象增加一些额外的职责。就功能来说,Decorator模式相比生成子类更灵活,在不改变接口的前提下可以增强类的功能,在如下场景可以考虑使用装饰模式:

  • 需要扩展一个类的功能,或给一个类增加附加责任
  • 动态地给一个对象增加功能,这些功能可以再动态撤销
  • 需要增加一些基本功能的排列组合而产生的非常大量的功能,从而使继承变得 不现实

装饰模式下存在以下几个角色:

  • 抽象构件:给出一个抽象接口,以规范准备接收附加责任的对象
  • 具体构件:定义一个将要接收附加责任的类
  • 装饰角色:持有一个构件对象的实例,并定一个与抽象构件一致的接口
  • 具体装饰角色:负责给构件对象添加附加责任

相关概念可参考:设计模式——装饰模式(Decorator)

// The constructor to decorate
function MacBook() {

  this.cost = function () { return 997; };
  this.screenSize = function () { return 11.6; };

}

// Decorator 1
function Memory( macbook ) {

  var v = macbook.cost();
  macbook.cost = function() {
    return v + 75;
  };

}

// Decorator 2
function Engraving( macbook ){

  var v = macbook.cost();
  macbook.cost = function(){
    return  v + 200;
  };

}

// Decorator 3
function Insurance( macbook ){

  var v = macbook.cost();
  macbook.cost = function(){
     return  v + 250;
  };

}

var mb = new MacBook();
Memory( mb );
Engraving( mb );
Insurance( mb );

// Outputs: 1522
console.log( mb.cost() );

// Outputs: 11.6
console.log( mb.screenSize() );
时间: 2024-09-26 11:52:32

JavaScript设计模式介绍的相关文章

JavaScript设计模式学习之“类式继承”

 这篇文章主要介绍了JavaScript设计模式学习之"类式继承",本文直接用代码实例讲解类式继承的实现方法,需要的朋友可以参考下     在做一件事情之前,首先要清楚做这件事情的好处有什么,相信不会有哪个人愿意无缘无故的去做事情.一般说来,我们在设计类的时候,实际上就是希望能减少重复性的代码,使用继承可以完美的做到这一点,借助继承机制,你可以在现有类的基础上再次进行设计并且充分利用它们已经具备的各种方法,而对设计的修改也更为轻松.废话不多说了,举例说明: 代码如下: function

Javascript设计模式之观察者模式的多个实现版本实例

 这篇文章主要介绍了Javascript设计模式之观察者模式的多个实现版本实例,本文给出3种实现版本代码,同时给出了Jquery实现版本,需要的朋友可以参考下     介绍 观察者模式又叫发布订阅模式(Publish/Subscribe),它定义了一种一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生变化时就会通知所有的观察者对象,使得它们能够自动更新自己. 使用观察者模式的好处: 1.支持简单的广播通信,自动通知所有已经订阅过的对象. 2.页面载入后目标对象很容易与观

javascript设计模式中的工厂模式示例

 这篇文章主要介绍了javascript设计模式中的工厂模式示例讲解,需要的朋友可以参考下 javaScript工厂方式原始的方式   因为对象的属性可以在对象创建后动态定义,这在 JavaScript 最初引入时都会编写类似下面的代码   代码如下: var oCar = new Object; oCar.color = "blue"; oCar.doors = 4; oCar.mpg = 25; oCar.showColor = function() {   alert(this.

《JavaScript设计模式》——导读

前言 JavaScript设计模式 设计模式是解决软件设计中常见问题的可复用方案.探索任何编程语言时,设计模式都是一个令人兴奋和极具吸引力的话题. 原因之一是:设计模式是许多先前开发人员总结出的经验,我们可以借鉴这些经验进行编程,以确保能够以优化的方式组织代码,为我们解决棘手的问题提供参考. 设计模式还是我们用来描述解决方案的常用词汇.当我们想要向其他人表述一种以代码形式构建解决方案的方式时,描述设计模式比描述语法和语义要简单得多. 在本书中,我们将探讨JavaScript编程语言中经典的与现代

JavaScript设计模式初探_javascript技巧

目的:设计模式众多,尝试记录下学到的不同设计模式的优劣,方便以后查阅. 前言:半年前看高程的时候看到设计模式这章,云里雾里,不是看不明白,而是不明白为啥要如此麻烦只为创建一个对象.直到最近完成了自己第一个小项目,才体会到当代码量多起来时没有适当的规范与限制是多么大的灾难.于是重新翻开高程,总结下几种我学到的简单设计模式的优劣. 正文:本文一共介绍7种设计模式以及他们的应用场景.优劣. 1.工厂模式 直接用函数来封装对象,将对象作为返回值. function person (name,age) {

《JavaScript设计模式》——9.12 Decorator(装饰者)模式

9.12 Decorator(装饰者)模式 Decorator是一种结构型设计模式,旨在促进代码复用.与Mixin相类似,它们可以被认为是另一个可行的对象子类化的替代方案. 通常,Decorator提供了将行为动态添加至系统的现有类的能力.其想法是,装饰本身对于类原有的基本功能来说并不是必要的:否则,它就可以被合并到超类本身了. 装饰者可以用于修改现有的系统,希望在系统中为对象添加额外的功能,而不需要大量修改使用它们的底层代码.开发人员使用它们的一个共同原因是,应用程序可能包含需要大量不同类型对

小议javascript 设计模式 推荐_javascript技巧

记得早前就说过要和大家分享"javascript设计模式",迟迟没写不是因为我懒,最近确实太忙,忙工作,忙旅游(啊哦?),好不容易这几天空闲了,接下来是兑现之前空口白话的时间了. 在讨论设计模式之前,请确认您已经有一定的脚本编程基础,如果不甚了解,建议可以先查阅本人很久之前写的这篇<浅谈javascript面向对象编程>请看下一篇文章. 讲到设计模式,不得不先重点着墨于"接口设计",因为接口设计在设计模式中的意义太大了,大于模式本身.直观起见,先介绍一下

JavaScript设计模式之单体模式全面解析_javascript技巧

单体是一个用来划分命名空间并将一些相关的属性与方法组织在一起的对象,如果她可以被实例化的话,那她只能被实例化一次(她只能嫁一次,不能二婚). 单体模式是javascript里面最基本但也是最有用的模式之一. 特点: 1. 可以用来划分命名空间,从而清除全局变量所带来的危险或影响. 2. 利用分支技术来来封装浏览器之间的差异. 3. 可以把代码组织的更为一体,便于阅读和维护. 单体模式的基本写法: /* 最基本的单体模式 */ var her = { name: 'Anna', sex: 'wom

javascript设计模式--策略模式之输入验证_javascript技巧

策略模式定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算饭的客户. 先定义一个简单的输入表单: <!DOCTYPE html> <html> <head> <meta charset="utf-"> <style> .form{ width: px; height: px; #margin: px auto; } .form-item-label{ width:px; text-align: