文档章节

设计模式学习

L
 LorinLuo
发布于 2017/07/05 18:02
字数 2163
阅读 31
收藏 0

准备知识:call、apply、闭包、高阶函数。

一、单例模式

        定义:保证一个类只有一个实例,并提供一个访问它的全局访问点。

        1. 用闭包实现单例模式

            优点:将single封装起来,不能手动去修改

            缺点:阅读不舒服,增加程序复杂性,而且构造函数负责了多项职责(创建对象和初始化,保证只有一个对象),违背单一职责原则

        var Single = (function () {
            var single = null;

            var Animal = function (name, age) {

              if (single) {
                    return single;
                }
                this.name = name;
                this.age = age;

                this.init();
               return  single = this;
            }

            Animal .prototype.init = function () {};

            return Animal;
        })();

        var obj = new Single("curry", 29);
        var obj1 = new Single("kaka", 35);

        console.log(obj === obj1); //true

    2、用代理实现单例模式

        优点:将普通类与单例的生成逻辑隔离开,实现了单一职责原则,普通类可以正常创建普通类的对象

        var ProxySingleton = (function () {
            var singleton = {};
            var classNameReg = /^function\s*(\w+)\s*\(/;

            return function () {
                var Class = [].shift.call(arguments);
                var classStr = Class.toString() + Math.random();
                var className = classNameReg.exec(classStr)[1];
                var obj = {};

                var args = arguments;

                function  SingletonClass () {

                    Class.apply(this, args );

                }

                SingletonClass.prototype = Class.prototype;

                if (singleton[className]) {
                    return singleton[className];
                }

                return singleton[className] = new SingletonClass();
            }
        })();

        function Animal (type, name) {
            this.type = type;
            this.name = name;
            this.init();
        }

        Animal.prototype.init = function () {

        };

        var dog = ProxySingleton(Animal, "dog", "dog-01");
        var cat = ProxySingleton(Animal, "cat", "cat-01");
        console.log(dog === cat); //true

3.惰性单例(需要的时候才创建单例,不是在初始化页面时创建单例)

var getSingle = function (fn) {

    var result;

    return function (){

        return result || (result = fn.apply(this, arguments));

    };

};

var createLoginLayer = function () {

    var div = document.createElement("div");

    div.innerHTML = "Login";

    return div;

};

var createSingleLoginLayer = getSingle(createLoginLayer );

var loginLayer = createSingleLoginLayer();

二、工厂模式

    定义:创建一类相似的对象

二、策略模式

    定义:定义一系列的算法,把他们一个个封装起来,并且使他们可以相互替换。

    1、计算奖金例子

    var strategies = {

        S: function (salary) {return return  * 4;},

        A: function (salary) {return return  * 3;},

         B: function (salary) {return return  * 2;}

    };

    var calculateBonus = function (level, salary) {

        return strategies [level](salary);

    };

    calculateBonus ("S", 20000); //80000

    calculateBonus ("A",10000); //30000

三、代理模式(一般代理对象要实现本体对象所有的接口)

    定义:为一个对象提供一个代用品或占位符,以便控制对它的访问。

    1、小明送花例子(通过B将花送给A)

        function Flower () {

        }

        var xiaoming = {
            sendFlower: function (target) {
                target.receiveFlower(new Flower());
            }
        };

        var B = {
            receiveFlower: function (flower) {
                A.listenGoodMood(function () {
                    A.receiveFlower(flower);
                });
            }
        };

        var A = {
            receiveFlower: function (flower) {
                console.log("收到花" + flower);
            },
            listenGoodMood: function (fn) {
                setTimeout(function () {
                    fn();
                }, 5000);
            }
        };

        xiaoming.sendFlower(B);

2.代理实图片预加载

        var myImage = (function () {
            var imgNode = document.createElement("img");

            document.body.appendChild(imgNode);

            return {
                setSrc: function (src) {
                    imgNode.src = src;
                }
            }
        })();

        var proxyImage = (function () {
            var img = document.createElement("img");

            img.onload = function () {
                myImage.setSrc(this.src);
            };

            return {
                setSrc: function (src) {
                    myImage.setSrc("file:// /C:/Users/svenzeng/Desktop/loading.gif");
                    img.src = src;
                }
            }
        })();

        proxyImage.setSrc("http://tupian.enterdesk.com/2013/xll/009/07/7/1.jpg");

3、虚拟代理合并HTTP请求(虚拟代理:把一些开销很大的对象,延迟到真正需要它的时候才去创建)

        function syncFile(id) {
            console.log("开始同步文件,id为:" + id);
        }

        var proxySyncFile = (function () {
            var cache = [], timer;

            return function (id) {
                cache.push(id);

                if (timer) {
                    return;
                }

                timer = setTimeout(function () { //三秒后向本体发送需要同步的ID集合
                    syncFile(cache.join(", "));
                    clearTimeout(timer);
                    timer = null;
                    cache.length = 0;
                }, 3000);
            }
        })();

        var checkbox = document.getElementsByTagName("input");

        for (var i = 0, c = checkbox[i++];) {
            c.onclick = function () {
                if (this.checked) {
                    proxySyncFile(this.id);
                }
            }
        }

    4、缓存代理

        function multi () {
            var value = 1;

            for (var i = 0, l = arguments.length; i < l; i++) {
                value *= arguments[i];
            }

            return value;
        }

        var proxyMulti = (function () {
            var cache = {};

            return function () {
                var args = [].join.call(arguments, ",");

                if (cache[args]) {
                    return cache[args];
                }

                return cache[args] = multi.apply(this, arguments);
            };
        })();

        proxyMulti(1, 2, 3);
        proxyMulti(1, 2, 3);

四、迭代器模式

    定义:提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。

    1、内部迭代器(each函数内部已经定义好迭代规则)

        function each (arr, callback) {

            for (var i = 0, l = arr.length; i < l; i++) {
               i f (callback.call(arr[i], i, arr[i]) === false) {

                    return false;    

                }
            }
        }

        each([1, 2, 3], function (i, n) {
            console.log(i, n);
        });

    2、外部迭代器(必须显示的请求迭代下一个元素,增强了迭代的灵活性,可以手动控制迭代过程或者顺序)

        function Iterater (obj) {
            var current = 0;

            function next () {
                current += 1;
            }

            function isDone () {
                return current >= obj.length;
            }

            function getCurrentItem () {
                return obj[current];
            }

            return {
                next: next,
                isDone: isDone,
                getCurrentItem: getCurrentItem
            };
        }

        function compare (iterater1, iterater2) {

            if  (iterater1.length !==iterater2.length ) {

                throw new Error("iterater1和iterater2不相等");

            }

            
            while (!iterater1.isDone() || !iterater2.isDone()) {
                if (iterater1.getCurrentItem() !== iterater2.getCurrentItem()) {
                    throw new Error("iterater1和iterater2不相等");
                }
                
                iterater1.next();
                iterater2.next();
            }

            console.log("iterater1和iterater2相等");
        }

        var iterater1 = Iterater([1, 2, 3]);
        var iterater2 = Iterater([1, 2, 3]);

        compare(iterater1, iterater2);

五、发布-订阅模式

    定义:又叫观察者模式,定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有倚赖于它的对象都将得到通知。我们一般用事件模型来替代传统的发布-订阅模式。

    1、简单例子    

    var event = {
            clientList: {},
            listen: function (key, callback) {
                if (!this.clientList[key]) {
                    this.clientList[key] = [];
                }

                this.clientList[key].push(callback)
            },
            remove: function (key, callback) {
                var callbacks = this.clientList[key];

                if (!callbacks) {
                    return false;
                }

                if (!callback) { //取消key对应消息的所有订阅
                    callbacks.length = 0;
                } else {
                    for (var i = callbacks.length - 1; i >= 0; i--) {
                        if (callbacks[i] === callback) {
                            callbacks.splice(i, 1);
                        }
                    }
                }
            },
            trigger: function () {
                var key = Array.prototype.shift.call(arguments);
                var callbacks = this.clientList[key];
                
                if (!callbacks || callbacks.length === 0) {
                    return false;
                }

                for (var i = 0, len = callbacks.length; i < len; i++) {
                    callbacks[i].apply(this, arguments);
                }
            }
        };

        function installEvent (obj) {
            for (var i in event) {
                obj[i] = event[i];
            }
        }

        var salesOffices = {};

        installEvent(salesOffices);

        salesOffices.listen("squareMeter88", function (price) {
            console.log("价格= " + price);
        });

        salesOffices.listen("squareMeter100", function (price) {
            console.log("价格= " + price);
        });

        salesOffices.trigger("squareMeter88", 1000000);
        salesOffices.trigger("squareMeter100", 1300000);

    2、Event作为中介者的角色,把订阅者和发布者联系起来

        var Event = {
            clientList: {},
            listen: function (key, callback) {
                if (!this.clientList[key]) {
                    this.clientList[key] = [];
                }

                this.clientList[key].push(callback)
            },
            remove: function (key, callback) {
                var callbacks = this.clientList[key];

                if (!callbacks) {
                    return false;
                }

                if (!callback) { //取消key对应消息的所有订阅
                    callbacks.length = 0;
                } else {
                    for (var i = callbacks.length - 1; i >= 0; i--) {
                        if (callbacks[i] === callback) {
                            callbacks.splice(i, 1);
                        }
                    }
                }
            },
            trigger: function () {
                var key = Array.prototype.shift.call(arguments);
                var callbacks = this.clientList[key];
                
                if (!callbacks || callbacks.length === 0) {
                    return false;
                }

                for (var i = 0, len = callbacks.length; i < len; i++) {
                    callbacks[i].apply(this, arguments);
                }
            }
        };

        Event.listen("squareMeter88", function (price) {
            console.log("价格= " + price);
        });

        Event.listen("squareMeter100", function (price) {
            console.log("价格= " + price);
        });

        Event.trigger("squareMeter88", 1000000);
        Event.trigger("squareMeter100", 1300000);

    3、加入缓存和命名空间

        var Event = (function () {
            var _default = "default",
                _listen,
                _trigger,
                _remove,
                _slice = Array.prototype.slice,
                _shift = Array.prototype.shift,
                _unshift = Array.prototype.unshift,
                namespaceCache = {},
                _create,
                each = function (ary, fn) {
                    var ret;

                    for (var i = 0, l = ary.length; i < l; i++) {
                        ret = fn.call(ary[i], i, ary[i]);
                    }

                    return ret;
                };

            _listen = function (key, fn, cache) {
                if (!cache[key]) {
                    cache[key] = [];
                }

                cache[key].push(fn);
            };

            _remove = function (key, cache, fn) {
                if (cache[key]) {
                    if (fn) {
                        for (var i = cache[key].length; i > 0; i--) {
                            if (cache[key][i] === fn) {
                                cache[key].splice(i, 1);
                            }
                        }
                    } else {
                        cache[key] = [];
                    }
                }
            };

            _trigger = function () {
                var cache = _shift.call(arguments),
                    key = _shift.call(arguments),
                    args = arguments,
                    _self = this,
                    ret,
                    stack = cache[key];

                if (!stack || !stack.length) {
                    return;
                }

                return each(stack, function () {
                    return this.apply(_self, args);
                });
            };

            _create = function (namespace) {
                var namespace = namespace || _default;
                var cache = {},
                    offlineStack = [], //离线事件
                    ret = {
                        listen: function (key, fn, last) {
                            _listen(key, fn, cache);

                            if (offlineStack === null) {
                                return;
                            }

                            if (last === "last") {
                                offlineStack.length && offlineStack.pop()();
                            } else {
                                each(offlineStack, function () {
                                    this();
                                })
                            }
                            offlineStack = null;
                        },
                        one: function (key, fn, last) {
                            _remove(key, cache);
                            this.listen(key, fn, last);
                        },
                        remove: function (key, fn) {
                            _remove(key, cache, fn);
                        },
                        trigger: function () {
                            var fn, args, _self = this;

                            _unshift.call(arguments, cache);
                            args = arguments;
                            fn = function () {
                                return _trigger.apply(_self, args);
                            };

                            if (offlineStack) {
                                return offlineStack.push(fn);
                            }

                            return fn();
                        }
                    };

                console.log(namespaceCache);

                return namespace ?
                    (namespaceCache[namespace] ? namespaceCache[namespace] :
                        namespaceCache[namespace] = ret)
                            : ret
            };

            return {
                create: _create,
                one: function (key, fn, last) {
                    var event = this.create();
                    event.one(key, fn, last);
                },
                remove: function (key, fn) {
                    var event = this.create();
                    event.remove(key, fn);
                },
                listen: function (key, fn, last) {
                    var event = this.create();
                    event.listen(key, fn, last);
                },
                trigger: function () {
                    var event = this.create();
                    event.trigger.apply(this, arguments);
                }
            };
        })();

        /******** 先发布后订阅 ************/
        Event.trigger("click", 1);
        Event.listen("click", function (a) {
            console.log(a);
        });

        /******** 使用命名空间 ************/
        Event.create("namespace1").listen("click", function (a) {
            console.log(a);
        });

        Event.create("namespace1").trigger("click", 2);

        Event.create("namespace1").listen("click", function (a) {
            console.log(a);
        });

六、命令模式(解耦命令请求者与命令接受者之间的强耦合关系,当我们不知道命令接受者是谁,以及被请求的操作是什么时,适合用命令模式)

    1、传统命令模式例子

        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>Document</title>
        </head>
        <body>
            <button id="refresh">refresh</button>
            <button id="add">add</button>
            <button id="del">del</button>
            <script type="text/javascript">
                var refreshBtn = document.getElementById("refresh");
                var addBtn = document.getElementById("add");
                var delBtn = document.getElementById("del");

                function setCommand (button, command) {
                    button.addEventListener("click", function () {
                        command.excute();
                    }, false);
                }

                var MenuBar = {
                    refresh: function () {
                        console.log("刷新菜单目录");
                    }
                };

                var SubMenu = {
                    add: function () {
                        console.log("增加子菜单");
                    },
                    del: function () {
                        console.log("删除子菜单");
                    }
                };

                function RefershMenuBarCommand (receiver) {
                    this.receiver = receiver;
                }

                RefershMenuBarCommand.prototype.excute = function () {
                    this.receiver.refresh();
                };

                function AddSubMenuCommand (receiver) {
                    this.receiver = receiver;
                }

                AddSubMenuCommand.prototype.excute = function () {
                    this.receiver.add();
                };


                function DelSubMenuCommand (receiver) {
                    this.receiver = receiver;
                }

                DelSubMenuCommand.prototype.excute = function () {
                    this.receiver.del();
                };

                var refershMenuBarCommand = new RefershMenuBarCommand(MenuBar);
                var addSubMenuCommand = new AddSubMenuCommand(SubMenu);
                var delSubMenuCommand = new DelSubMenuCommand(SubMenu);

                setCommand(refreshBtn, refershMenuBarCommand);
                setCommand(addBtn, addSubMenuCommand);
                setCommand(delBtn, delSubMenuCommand);
            </script>
        </body>
        </html>

    2. js命令模式(闭包实现)

        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>Document</title>
        </head>
        <body>
            <button id="refresh">refresh</button>
            <button id="add">add</button>
            <button id="del">del</button>
            <script type="text/javascript">
                function setCommand (btn, command) {
                    btn.onclick = function () {
                        command.excute();
                    };
                };
                var Menubar = {
                    refresh: function () {
                        console.log("刷新菜单界面");
                    }
                };
                function RefreshMenubarCommand (receiver) {
                    return {
                        excute: function () {
                            receiver.refresh();
                        }
                    };
                };

                var refreshMenubarCommand = RefreshMenubarCommand(Menubar);
                setCommand(document.getElementById("refresh"), refreshMenubarCommand);
            </script>
        </body>
        </html>

    3. 执行和撤销

        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>Document</title>
            <style type="text/css">
                #ball {
                    position: absolute;
                    top: 100px;
                    left: 0;
                    width: 100px;
                    height: 100px;
                    border-radius: 50%;
                    background: #eee;
                }
            </style>
        </head>
        <body>
            <input type="text" id="pos">
            <button id="excute">excute</button>
            <button id="uodo">uodo</button>
            <select id="direction">
                <option value="top">top</option>
                <option value="bottom">bottom</option>
                <option value="left" selected>left</option>
                <option value="right">right</option>
            </select>
            <div id="ball"></div>
            <script type="text/javascript">
                var ball = document.getElementById("ball");
                var direction = document.getElementById("direction");
                var pos = document.getElementById("pos");
                var excuteBtn = document.getElementById("excute");
                var uodoBtn = document.getElementById("uodo");
                var moveCommandStack = [];
                var ballAction = {
                    dom: ball,
                    move: function (direction, pos) {
                        this.dom.style[direction] = pos + "px";
                    }
                };

                function MoveCommand(recevier, attr, pos) {
                    this.recevier = recevier;
                    this.pos = pos;
                    this.attr = attr;
                    this.oldPos = this.recevier.dom.getBoundingClientRect()[attr];
                }

                MoveCommand.prototype.excute = function () {
                    this.recevier.move(this.attr, this.pos);
                };
                MoveCommand.prototype.uodo = function () {
                    this.recevier.move(this.attr, this.oldPos);
                };

                excuteBtn.onclick = function () {
                    var value = pos.value;
                    var directionStr = direction.selectedOptions[0].value;
                    var command;

                    if (!value || !direction) {
                        return false;
                    }

                    command = new MoveCommand(ballAction, directionStr, pos.value);
                    command.excute();
                    moveCommandStack.push(command);
                };
                uodoBtn.onclick = function () {
                    var command = moveCommandStack.pop();

                    if (!command) {
                        return false;
                    }

                    command.uodo();
                };
            </script>
        </body>
        </html>

二、原型模式(克隆)

    Object.create = Object.create || function (obj) {

        function Clone () {}

        Clone.prototype = obj;

        return new Clone();

    }

 

© 著作权归作者所有

共有 人打赏支持
上一篇: 深拷贝
L
粉丝 18
博文 166
码字总数 116776
作品 0
成都
程序员
私信 提问

暂无文章

容器服务

简介 容器服务提供高性能可伸缩的容器应用管理服务,支持用 Docker 和 Kubernetes 进行容器化应用的生命周期管理,提供多种应用发布方式和持续交付能力并支持微服务架构。 产品架构 容器服务...

狼王黄师傅
昨天
3
0
高性能应用缓存设计方案

为什么 不管是刻意或者偶尔看其他大神或者大师在讨论高性能架构时,自己都是认真的去看缓存是怎么用呢?认认真真的看完发现缓存这一块他们说的都是一个WebApp或者服务的缓存结构或者缓存实现...

呼呼南风
昨天
12
0
寻找一种易于理解的一致性算法(扩展版)

摘要 Raft 是一种为了管理复制日志的一致性算法。它提供了和 Paxos 算法相同的功能和性能,但是它的算法结构和 Paxos 不同,使得 Raft 算法更加容易理解并且更容易构建实际的系统。为了提升可...

Tiny熊
昨天
2
0
聊聊GarbageCollectionNotificationInfo

序 本文主要研究一下GarbageCollectionNotificationInfo CompositeData java.management/javax/management/openmbean/CompositeData.java public interface CompositeData { public Co......

go4it
昨天
3
0
阿里云ECS的1M带宽理解

本文就给大家科普下阿里云ECS的固定1M带宽的含义。 “下行带宽”和“上行带宽” 为了更好的理解,需要先给大家解释个词“下行带宽”和“上行带宽”: 下行带宽:粗略的解释就是下载数据的最大...

echojson
昨天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部