JavaScript设计模式

Constructor(构造器)模式

//创建一个Person类
function Person(name,age,sex){
    this.name = name;
    this.age = age;
    this.sex = sex;

    this.toString = function(){
        return 'name:'+ this.name + '\tage:' + this.age + '\tsex:' + this.sex;
    }
}

//实例化一个Person对象
var person = new Person('niki',24,'男');
console.info(person.toString());

Module(模块)模式

优点

  • 对代码进行了封装,对于y拥有面向对象背景的开发人员来说更加整洁。
  • 支持私有数据。

缺点

  • 由于访问公有和私有成员的方法不同,当我们想改变可见性时需要一一去修改。
  • 无法为私有成员创建自动化单元测试,BUG需要修订补丁是会增加额外的复杂性。
  • 无法轻易地扩展私有方法
//申明一个登陆控制器并返回一个对象
var loginController = (function(){
        //私有变量
        var loginUrl = 'http://localhost/user/login';//登陆地址
        var checkUrl = 'http://localhost/user/check';//检查地址

        return {
            isLogined : false,//是否已登陆

            //用户是否存在
            checkUser:function(username,password){
                if(username === 'admin'){
                    return true;
                }else{
                    return false;
                }
            },

            //用户登陆
            loginUser:function(username,password){
                if(username === 'admin' && password === '123456'){
                    return true;
                }else{
                    return false;
                }
            }   
        };
    })();

    //调用方法
    console.info(loginController.checkUser('admin'));

Revealing Module(揭示模块)模式

优点

  • 使脚本语法更加一致
  • 在模块底部更容易获取那些方法和变量可以被公开访问,从而改善可读性

缺点

  • 当一个私有函数引用一个公有函数,在需要打补丁时公有函数不能被覆盖。
  • 该模式只适用于函数,不适用于公有成员
var user = (function() {
    var name;//私有变量

    //获取私有变量
    function getNameFunc() {
        return name;
    }

    //设置私有变量值
    function setNameFunc(value) {
        name = value;
    }

    return {
        getName: getNameFunc,
        setName: setNameFunc
    };
})();
user.setName("niki");
console.info(user.getName());

Singleton(单例)模式

var mySingleton = (function(){
    var instance;//用来存放实例

    //实例初始化
    function init(){
        function privateMethod(){
            console.info("I'm private");
        }
        var privateVariable = "I'm also private";
        var privateRandomNumber = Math.random();
        return{
            publicMethod:function(){
                console.info("I'm public");
            },
            publicVariable:"I'm alse public",
            getRandomNumber:function(){
                return privateRandomNumber;
            }
        };
    }

    return {
        //获取实例
        getInstance:function(){
            if(!instance){
                instance = init();
            }
            return instance;
        }
    }
})();
var instanceA = mySingleton.getInstance();
var instanceB = mySingleton.getInstance();
var a = instanceA.getRandomNumber();
var b = instanceB.getRandomNumber();
console.info(a + " --- " + b);//a等于b,因为是同一个实例

Observer(观察者)模式

观察者列表操作类

function ObserverList(){
    this.observerList = [];
}
ObserverList.prototype.add = function(obj){
    return this.observerList.push(obj);
};
ObserverList.prototype.empty = function(){
    this.observerList = [];
};
ObserverList.prototype.count = function(){
    return this.observerList.length;
};
ObserverList.prototype.get =function(index){
    if(index>-1 && index<this.observerList.length){
        return this.observerList[index];
    }
};
ObserverList.prototype.insert =function(obj,index){
    var pointer = -1;
    if(index === 0){
        this.observerList.unshift(obj);
        pointer = index;
    }else if(index === this.observerList.length){
        this.observerList.push(obj);
        pointer = index;
    }
    return pointer;
};
ObserverList.prototype.indexOf = function(obj,startIndex){
    var i = startIndex,pointer = -1;
    while(i < this.observerList.length){
        if(this.observerList[i] === obj){
            pointer = i;
        }
        i++;
    }
    return pointer;
};
ObserverList.prototype.removeIndexAt = function(index){
    if(index === 0){
        this.observerList.shift();
    }else if(index === this.observerList.length - 1){
        this.observerList.pop();
    }
};

模拟目标类

function Subject(){
    this.observers = new ObserverList();
}
Subject.prototype.addObserver = function(observer){
    this.observers.add(observer);
};
Subject.prototype.removeObserver = function(observer){
    this.observers.removeIndexAt(this.observers.indexOf(observer,0));
};
Subject.prototype.notify = function(context){
    var observerCount = this.observers.count();
    for(var i=0;i<observerCount;i++){
        this.observers.get(i).update(context);
    }
};

观察者类

function Observer(){
    this.update = function(context){

    };
}

案例所需HTML源码

<button id="addNewObserver">Add New Oberver checkbox</button>
<input type="checkbox" name="" id="mainCheckbox"/>
<div id="observerContainer"></div>

实例

var controllerCheckbox = document.getElementById("mainCheckbox"),
addBtn = document.getElementById("addNewObserver"),
container = document.getElementById("observerContainer");

//具体目标
extend(new Subject(),controllerCheckbox);

//点击checkbox会触发到观察者上
controllerCheckbox["onclick"] = new Function("controllerCheckbox.notify(controllerCheckbox.checked)");

addBtn["onclick"] = AddNewObserver;

//具体观察者
function AddNewObserver(){
    var check = document.createElement("input");
    check.type="checkbox";
    extend(new Observer(),check);

    check.update = function(value){
        this.checked = value;
    };

    //为主subject的观察者列表添加观察者 
    controllerCheckbox.addObserver(check);
    container.appendChild(check);
}
function extend(obj,extension){
    for(var key in obj){
        extension[key] = obj[key];
    }
}

Publish/Subscribe(发布/订阅)模式

Publish/Subscribe 的实现

var pubsub = {};
(function(q){
    var topics = {},
    subUid = -1;

    //发布或广播事件
    q.publish = function(topic,args){
        if(!topics[topic]){
            return false;
        }
        var subscribes = topics[topic],
        len = subscribes ? subscribes.length : 0;
        while(len--){
            subscribes[len].func(topic,args);
        }
        return false;
    };

    //通过特定的名称和回调函数订阅事件
    q.subscribe = function(topic,func){
        if(!topics[topic]){
            topics[topic] = [];
        }
        var token = (++subUid).toString();
        topics[topic].push({
            token:token,
            func:func
        });
        return token;
    };

    //基于订阅上的标记引用
    q.unsubscribe = function(token){
        for(var m in topics){
            if(topics[m]){
                for(var i=0,j=topics[m].length;i<j;i++){
                    if(topics[m][i].token === token){
                        topics[m].splice(i,1);
                        return token;
                    }
                }
            }
        }
        return this;
    };
})(pubsub);

使用案例

var messageLogger = function(topics,data){
    console.info("Logging:" + topics + "-" + data);
};
var subscription = pubsub.subscribe("inbox/newMessage",messageLogger);
pubsub.publish("inbox/newMessage","Hello World");

Mediator(中介者)模式

var meidator = (function(){
    //存储可被广播或监听的topic
    var topics = {};

    //订阅一个topic
    var subscribe = function(topic,fn){
        if(!topics[topic]){
            topics[topic] = [];
        }
        topics[topic].push({context:this,callback:fn});
        return this;
    };

    //发布或广播事件到程序的剩余部分
    var publish = function(topic){
        var args;

        if(!topics[topic]){
            return false;
        }

        args = Array.prototype.slice.call(arguments,1);

        for(var i=0,len=topics[topic].length; i<len; i++){
            var subscription = topics[topic][i];
            subscription.callback.apply(subscription.context,args);
        }
        return false;
    };

    return {
        publish:publish,
        subscribe:subscribe,
        installTo:function(obj){
            obj.publish = publish;
            obj.subscribe = subscribe;
        }
    };
})();

Prototype(原型)模式

Object.create方式

  Object.create方法有两个参数。参数一必选,代表需要实例化的一个对象;参数二不是必选,当我们提供第二个参数时候,该方法代表了实例化了一个参数二的对象并继承了参数一对象。

var Car = {
    name:"Ford Escort",

    drive:function(){
        console.info("Weeee.I'm driving");
    },

    panic:function(){
        console.info("Wait,How do you stop this thing");
    }
};

//var myCar = Object.create(Car);
var myCar = Object.create(Car,{
    "speed":{
        value:250,
        enumerable:true
    }
});
console.info(myCar.name);
console.info(myCar.speed);
myCar.drive();

prototype 方式

var vehiclePrototype = {
    init:function(model){
        this.model = model;
    },
    getModel:function(){
        console.info(this.model);
    }
};

function vehicle(model){
    function F(){};
    F.prototype = vehiclePrototype;
    var f = new F();
    f.init(model);
    return f;
}
var car = vehicle("Ford Escort");
car.getModel();

Command(命令)模式

var CarManage = {
    requestInfo :function(model,id){
        console.info("requestInfo\tmodel:" + model + "\tid:" + id);
    },
    buyCar:function(model,id){
        console.info("buyCar\tmodel:" + model + "\tid:" + id);
    },
    arrangeViewing:function(model,id){
        console.info("arrangeViewing\tmodel:" + model + "\tid:" + id);
    },
    execute:function(name){
        return CarManage[name] && CarManage[name].apply(CarManage,Array.prototype.slice.call(arguments,1));
    }
};

CarManage.execute("requestInfo","Ferrari","100");
CarManage.execute("buyCar","Bens","101");
CarManage.execute("requestInfo","Ford","102");
CarManage.execute("arrangeViewing","BMW","103");

Facade(外观)模式

作用

  Facade模式是一种结构型模式,为更大的代码体提供一个方便的高层次接口,能够隐藏其底层的真实复杂性。
  Facade模式既能简化类的接口,也能将这个类从使用它的代码中解耦出来。这使我们能够直接与子系统交互,这种方式相比直接访问子系统有时不易犯错误。易于使用和实现该模式的时占用空间小。

var module = (function(){
    var _private = {
        i : 5,
        get : function(){
            return this.i;
        },
        set:function(value){
            this.i = value;
        },
        run:function(){
            console.info(this.i);
        }
    };
    return {
        facade:function(args){
            var val = _private.get();
            _private.set(val + args.val);
            _private.run();
        }
    };
})();
module.facade({name:"niki",val:10});

Factory(工厂)模式

//声明水果对象
function Fruit(options){
    this.name = options.name || "fruit";
    this.color = options.color || "red";
    this.price = options.price || 5.00;
    this.sell = function(){
        console.info("Sell Fruit:" + this.name + "\tcolor:" + this.color  + "\tprice:" + this.price);
    }
}
//声明一个蔬菜对象
function Vegetable(options){
    this.name = options.name || "vegetable";
    this.color = options.color || "yellow";
    this.price = options.price || 10.50;
    this.sell = function(){
        console.info("Sell Vegetable:" + this.name + "\tcolor:" + this.color  + "\tprice:" + this.price);
    }
}

//声明一个超市工厂
function MarketFactory(){
    //添加默认货物对象
    MarketFactory.prototype.goodsClass = Fruit;

    //创建货物对象
    MarketFactory.prototype.addGoods = function(options){
        if(options.type === "Fruit"){
            this.goodsClass = Fruit;
        }else{
            this.goodsClass = Vegetable;
        }
        return new this.goodsClass(options);//实例化货物
    };
}
//实例化一个超市工厂
var marketFactory = new MarketFactory();
//实例化一个苹果对象
var apple = marketFactory.addGoods({type:"Fruit",name:"apple",color:"red",price:4.98});
apple.sell();

//实例化一个花椰菜对象
var broccoli = marketFactory.addGoods({type:"Vegetable",name:"broccoli",color:"green",price:14.50});
broccoli.sell();

何时使用Factory 模式

  • 当对象或组件设置涉及搞复杂性时
  • 当需要根据所在的不同环境轻松生成对象的不同实例时
  • 当处理很多共享相同属性的小型对象或组件时
  • 在编写只需要满足一个API契约(亦称鸭子类型)的其他对象的实例对象时。对解耦是很有用的

何时不应使用Factory 模式

  如果应用错误,这种模式会为应用程序带来大量不必要的复杂性。除非创建对象提供一个接口是我们正在编写的库或框架的设计目标,否则建议坚持使用显示构造函数,以避免不必要的开销。

  由于对象创建的过程实际上是藏身接口之后抽象出来的,单元测试也可能带来问题,这取决于对象创建的过程有多复杂
Abstract Factory (抽象工厂)

function Fruit(options){
    this.name = options.name || "fruit";
    this.color = options.color || "red";
    this.price = options.price || 5.00;
    this.sell = function(){
        console.info("Sell Fruit:" + this.name + "\tcolor:" + this.color  + "\tprice:" + this.price);
    }
}

function Vegetable(options){
    this.name = options.name || "vegetable";
    this.color = options.color || "yellow";
    this.price = options.price || 10.50;
    this.sell = function(){
        console.info("Sell Vegetable:" + this.name + "\tcolor:" + this.color  + "\tprice:" + this.price);
    }
}
var AbstractMarketFactory = (function(){
    var types = {};
    return {
        getGoods:function(type,options){
            var goods = types[type];
            return goods ? new goods(options) : null;
        },
        registerGoods:function(type,goods){
            types[type] = goods;
        }
    };
})();

AbstractMarketFactory.registerGoods("fruit",Fruit);
AbstractMarketFactory.registerGoods("vegetable",Vegetable);

var apple = AbstractMarketFactory.getGoods("fruit",{type:"Fruit",name:"apple",color:"red",price:4.98});
apple.sell();

var broccoli = AbstractMarketFactory.getGoods("vegetable",{type:"Vegetable",name:"broccoli",color:"green",price:14.50});
broccoli.sell();

Mixin(混入)模式

子类化

var Person = function(firstName,lastName){
    this.firstName = firstName;
    this.lastName = lastName;
    this.gender = "male";
};

var SuperHero = function(firstName,lastName,powers){
    Person.call(this,firstName,lastName);
    this.powers = powers;
};
SuperHero.prototype = Object.create(Person.prototype);
var superman = new SuperHero("Clark","Kent",["flight","heat-vision"]);
console.info(superman);

Mixin(混入)

var Car = function(settings) {
    this.model = settings.model || "no model provided";
    this.color = settings.color || "no color provided";
};
var Mixin = function() {};
Mixin.prototype = {
    driveForward: function() {
        console.info("drive forward");
    },
    driveBackward: function() {
        console.info("drive backward");
    },
    driveSideways: function() {
        console.info("drive sideways");
    }
};

function augment(receivingClass,givingClass){
    if(arguments[2]){
        for(var i=2,len=arguments.length; i<len;i++){
            receivingClass.prototype[arguments[i]] =  givingClass.prototype[arguments[i]];
        }
    }else{
        for(var methodName in givingClass.prototype){
            //方式一
            if(!Object.hasOwnProperty(receivingClass.prototype,methodName)){
                receivingClass.prototype[methodName] = givingClass.prototype[methodName];
            }
            //方式二
            // if(!receivingClass.prototype[methodName]){
            // receivingClass.prototype[methodName] = givingClass.prototype[methodName];
            // }
        }
    }
}

augment(Car,Mixin,"driveForward","driveBackward")

var car = new Car({
    model:"Ford Escort",
    color:"red"
});
car.driveForward();
car.driveBackward();
// car.driveSideways();//因为没有继承,所以会报错

优点和缺点

Mixin有助于减少系统中的重复功能以及增加函数复用。当一个应用程序可能需要在各种对象实例中共享行为时,我们可以通过在Mixin中维持这种共享功能并专注于实现系统中真正不同的功能,来轻松避免任何重复。

当然,Mixin会导致原型污染和函数起源方面的不确定性

Decorator(装饰者)模式

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

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

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

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

var mb = new MacBook();
Memory(mb);
Engraving(mb);
Insurance(mb);
console.info(mb.cost());
console.info(mb.screenSize());

Flyweight(享元)模式

你可能感兴趣的:(JavaScript,设计模式,模式)