设计模式的介绍与应用

一、设计模式的介绍

1、设计模式是可重用的用于解决软件设计中一般问题的方案;
2、设计模式和语言没关系,就像面向对象编程,它是一种思想;
3、设计模式同样也为我们描述问题,提供了通用的词汇

二、怎么才能称之为一个设计模式

模式包含的要素
1、模式名称
2、该模式所能解决的问题
3、解决方案
4、使用该模式后 的效果(优点和缺点)

三、设计模式的好处

1、 模式是行之有效的解决方法:
他们提供固定的解决方法来解决在软件开发中出现的问题,这些都是久经考验的反应了开发者的经验和见解的使用模式来定义的技术。

2、模式可以很容易地重用:
一个模式通常反映了一 个可以适应自己需要的开箱即用的解决方案。这个特性让它们很健壮。

3、 模式善于表达:
当我们看到一个提供某种解决方案的模式时, -般有一组结构和词汇可以非常优雅地帮助表达相当大的解决方案。

四、设计模式分类

1、创建模式:涉及对象的创建

1-1、单例模式

单例模式的定义是产生一个类的唯一 实例,提供唯一的命名空间用来管理命名空间。可以减少网页中全局变量的数量,控制变量命名的冲突。

单例模式应用:

需求-现在在页面的很多模块都需要弹出遮罩层
目前问题-遮罩层都是在弹出的时候创建,重复点击就会创建多个,虽然可以通过remove来移出节点,但是这个是不合理的。
目标-重头到尾都只显示同一个遮罩层
解决方法:我们可以通过单例模式管理我们的遮罩层,如果遮罩层没有被创建过,我们就创建一个新遮罩层,如果遮罩层已经创建了,就直接返回遮罩层的引用。

代码示例:

<style type="text/css">
        .zheZhao {
            position: absolute;
            left: 0;
            top: 100px;
            width: 100%;
            height: 100%;
            background-color: rgba(6, 6, 6, .2);
        }
    </style>
<button id="btn1">弹出遮罩层</button>

调用封装好的方法,创建遮罩层

<script src="../js/zheZhao.js"></script>
<script type="text/javascript">
    document.getElementById('btn1').addEventListener('click', () => {
    //调用封装好的方法,创建遮罩层
        createZheZhao()
    })
</script>

将遮罩层进行了封装

(function () {

    let zheZhaoDiv;

    function createDiv() {
        if (!zheZhaoDiv) {//判断遮罩层是否已经存在,若不存在,才创建遮罩
            zheZhaoDiv = document.createElement('div');
            zheZhaoDiv.className = 'zheZhao'
            document.body.appendChild(zheZhaoDiv)
        }
        zheZhaoDiv.style.display = 'block'
    }

    window.createZheZhao = createDiv;
})()
1-2、工程模式

工厂模式是由一个方法来决定到底要创建哪个类的实例,而这些实例经常都拥有相同的接口.这种模式主要用在所实例化的类型在编译期并不能确定,而是在执行期决定的情况。说的通俗点,就像公司茶水间的饮料机,要咖啡还是牛奶取决于你按哪个按钮。I解决方法:我们可以通过单例模式管理我们的遮罩层,如果遮罩层没有被创建过,我们就创建一个新遮罩层,如果遮罩层已经创建了,就直接返回遮罩层的引用。

工厂模式应用:

需求-在自己封装的ajax里面,需要创建一个xhr对象
目前问题- xhr对象创建方法有2个,我不清楚创建的条件是什么,我只知道自己需要xhr对象
目标-给我返回创建好的xhr对象
解决方法:我们可以通过工模式“生产" xhr对象,至于交给哪个方法去“生产”,由工厂自己决定。

代码示例:

    let ajax = function () {
        //通过工厂自己觉得使用哪一套生产方式,只需要知道最后自己能够得到xhr对象就行了
        let newXhr = xhrFactory();
        console.log(newXhr);
    }
    ajax();
let xhrFactory=(function (){
     return function _xhrFactory() {
        let xhr = null;
        if (window.XMLHttpRequest) {
            xhr = new XMLHttpRequest();
        } else if (window.ActiveXObject) {
            xhr = new ActiveXObject('Microsoft.XMLHTTP');
        }
        return xhr
    }
})

1-3、建造者模式
1-4、原型模式

2、结构模式:设计类和对象的组合

2-1、外观模式

外观模式(门面模式),是一 种相对简单而又无处不在的模式。外观模式提供一 个高层接口,这个接口使得客户端或子系统更加方便调用。通过另一个渠道去获取我们想要的属性

思考:现有对象emp,有name属性。属性获取emp.name,这种属性可以直接使用,也可以直接赋值

外观模式应用:

需求-通过提供的方式去获取/设置对象,但是必须按照我的方法来
目前问题-直接暴露可能会导致数据有验证问题,怎么才能又访问到,又不破坏呢?
目标-变成私有化,又要能获取到这个属性
解决方法:我们可以通过外观模式来调用get/set方法

代码示例:

    function emp() {
        let name;
        this.getName = function () {
            return name
        }
        this.setName = function (value) {
            name = value
        }
    }

    let person = new emp();
    person.setName('张三');
    console.log(person.getName());//张三
2-2、装饰者模式

装饰者模式:
在原有的功能上已经不能满足用户的需求,需要在原有功能上添加新功能但是不影响原有功能;
在装饰者模式里面可以原封不动的使用,不需要知道原有方法实现的具体细节,只需要直接添加;

装饰者模式应用:

需求-给页面,上的按钮添加新功能,如果按钮已经有onclick事件,继续保留原onclick事件的事件处理函数,并且把新功能添加在后面
目前问题-不知道那些按钮已有onclick,一个个查找很耗时间,效率太低
目标-无需知道是否已有onclick,保留原来的事件处理程序,并且新增函数
解决方法:我们可以通过装饰者模式来保存原有东西,并直接添加新内容

代码示例:

<button id="btn1">点击一下</button>
<button id="addBtn">给btn1添加新功能</button>
<script type="text/javascript">
    let btn1 = document.getElementById('btn1')
    btn1.addEventListener('click', function () {
        console.log('这是按钮原有的点击事件');
    })
    let addBtn = document.getElementById('addBtn');
    addBtn.addEventListener('click', function () {
        //给btn1添加新功能
        let oldClick = btn1.onclick == undefined ? function () {
        } : btn1.onclick;//获取到原来的点击功能
        btn1.addEventListener('click', function () {
            oldClick();//执行原来的老方法
            //    添加新功能
            console.log('这是我给btn1添加的新功能');
        })
    })
</script>

在这里插入图片描述
打印结果
打印结果

2-3、适配器模式

适配器模式用来适配2个类接口不兼容的问题比如2个不同的代码库,前后端数据问题等。比如数据传递上,原始数据是一个字符串,但是在jQuery中通过ajax传递数据需要为object类型,这个时候就可以通过适配器转换数据对数据进行拆分重新包装以适配jQ格式来传递数据

适配器模式应用:

需求-把传入两对象转化为字符串以便ajax传送数据使用
目前问题-对象直接传递,需要经过处理才能满足前后台数据传送格式
目标-按照格式转化数据
解决方法:我们可以通过适配器模式来单独定义一个转化数据的函数,通过这个函数适配2个不同的格式

代码示例

链接: 前后端数据不同的适配

3、行为模式:

刻画了类和对象交换及分配职责的方式,主要目标是解耦

3-1、桥接模式

桥接模式可以对相同的逻辑做抽象提取处理,代码更简洁,重用率更大,可读性更高通过提取创建一个单独的函数,解除与事件中的this的耦合桥接模式=事件与业务逻辑之间的桥梁。

桥接模式应用:

需求-在多个class上需要实现移入移出的效果
目前问题- 2个不同的事件- onmouseover,
onmouseout但是差不多的内容-改变颜色&背
景颜色,只是改变的值不一样,代码冗余
目标-简化代码,让事件与程序之间解耦达到重用
解决方法:我们可以通过桥接模式来连接事件和处理程序,通过参数传递达到函数重用多样化。

代码示例

    <style type="text/css">
        div {
            border: 1px solid brown;
            margin: 10px;
            height: 100px;
            width: 100px;
        }
    </style>
<div id="div1">这是div1</div>
<div id="div2">这是div2</div>
<script type="text/javascript">
    let div1 = document.getElementById('div1');
    let div2 = document.getElementById('div2');
    //鼠标移入
    div1.onmouseover = function () {
        changeStyle(div1, 'blue', 'white')
    }
    //鼠标移出
    div1.onmouseout = function () {
        changeStyle(div1, 'red', 'black')
    }//鼠标移入
    div2.onmouseover = function () {
        changeStyle(div2, 'green', 'blue')
    }
    //鼠标移出
    div2.onmouseout = function () {
        changeStyle(div2, 'yellow', 'gray')
    }
    //改变样式
    function changeStyle(node, bgColor, color) {
        node.style.backgroundColor = bgColor;
        node.style.color = color;
    }
</script>

移入时的效果:
在这里插入图片描述
在这里插入图片描述
移出时的效果:
在这里插入图片描述

3-2、状态模式

示例代码

<button onclick="active('jump')">跳</button>
<button onclick="active('run')">跑</button>
<button onclick="active('walk')">走</button>

<script type="text/javascript">
    //    状态模式
    let obj = {
        jump: function () {
            console.log('跳');
        },
        run: function () {
            console.log('跑');
        },
        walk: function () {
            console.log('走');
        }
    }

    function active(type) {
        obj[type]();
    }
</script>

在这里插入图片描述

3-3、观察者模式

在这里插入图片描述
示例代码

 //宝宝(观察的对象)
    class Subject {
        constructor(name) {
            this.name = name;
            this.state = '开心';
            this.observer = [];//存放观察者
        }

        //观察者和宝宝建立联系
        attach(ther) {
            this.observer.push(ther);
        }
        setState(value){
            this.state=value;
            this.observer.forEach((item)=>{
                item.update(this)
            })
        }
    }

    //    观察者
    class Observer {
        constructor(name) {
            this.name = name;
        }

        update(subject) {
            console.log(this.name + '观察的对象' + subject.name + '他的心情是' + subject.state);
        }
    }

    let baby = new Subject('小宝宝')
    let father = new Observer('爸爸')
    let mother = new Observer('妈妈')

    baby.attach(father);
    baby.attach(mother);

    baby.setState('我很开心')

在这里插入图片描述

3-4、发布订阅者模式

在这里插入图片描述
代码示例

<script type="text/javascript">
    /*
    * on:订阅
    * emit:发布
    */
    let publishingHouse = {
        _authorArr: [],//所有的订阅者
        on(author) {//订阅
            this._authorArr.push(author);
        },
        emit(value) {//发布
            //    将发布内容通知给所有的订阅者
            this._authorArr.forEach((item) => {
                item.notice(value)
                // item.msg=value;
                // console.log(item.msg);
            })
        }
    }
    //订阅
    publishingHouse.on({name: '小方同学', msg: '',notice(value){
            console.log(this.name+'收到消息:'+value)}});
    publishingHouse.on({name: '小琦同学', msg: '',notice(value){
            console.log(this.name+'收到消息:'+value)}});
    publishingHouse.on({name: '芳芳', msg: '',notice(value){
            console.log(this.name+'收到消息:'+value)}});
    //发布
    publishingHouse.emit('成都高温天气橙色预警,注意中暑');
    publishingHouse.emit('重要通知:下周要下雨了哟');

</script>

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值