javascript设计模式(2)

1、组合模式

定义

是用小的子对象来构建更大的 对象,而这些小的子对象本身也许是由更小 的“孙对象”构成的。

核心

可以用树形结构来表示这种“部分- 整体”的层次结构。

调用组合对象 的execute方法,程序会递归调用组合对象 下面的叶对象的execute方法

但要注意的是,组合模式不是父子关系,它是一种聚合的关系,将请求委托给 它所包含的所有叶对象。基于这种委托,就需要保证组合对象和叶对象拥有相同的 接口

此外,也要保证用一致的方式对待 列表中的每个叶对象,即叶对象属于同一类,不需要过多特殊的额外操作

实现

使用组合模式来实现扫描文件夹中的文件

// 文件夹 组合对象
function Folder(name) {
    this.name = name;
    this.parent = null;
    this.files = [];
}

Folder.prototype = {
    constructor: Folder,

    add: function(file) {
        file.parent = this;
        this.files.push(file);

        return this;
    },

    scan: function() {
        // 委托给叶对象处理
        for (var i = 0; i < this.files.length; ++i) {
            this.files[i].scan();
        }
    },

    remove: function(file) {
        if (typeof file === 'undefined') {
            this.files = [];
            return;
        }

        for (var i = 0; i < this.files.length; ++i) {
            if (this.files[i] === file) {
                this.files.splice(i, 1);
            }
        }
    }
};

// 文件 叶对象
function File(name) {
    this.name = name;
    this.parent = null;
}

File.prototype = {
    constructor: File,

    add: function() {
        console.log('文件里面不能添加文件');
    },

    scan: function() {
        var name = [this.name];
        var parent = this.parent;

        while (parent) {
            name.unshift(parent.name);
            parent = parent.parent;
        }

        console.log(name.join(' / '));
    }
};

扫描结果,输出了文件所在路径:

4. 优缺点

优点

可 以方便地构造一棵树来表示对象的部分-整体 结构。在树的构造最终 完成之后,只需要通过请求树的最顶层对 象,便能对整棵树做统一一致的操作。

缺点

创建出来的对象长得都差不多,可能会使代码不好理解,创建太多的对象对性能也会有一些影响

2、 中介者模式

  1. 定义

所有的相关 对象都通过中介者对象来通信,而不是互相引用,所以当一个对象发生改变时,只需要通知中介者对象即可

  1. 核心

使网状的多对多关系变成了相对简单的一对多关系(复杂的调度处理都交给中介者)

使用中介者后:

3 、外观模式

在类似场景中,这些例子有以下特点:

1. 一个统一的外观为复杂的子系统提供一个简单的高层功能接口;

2. 原本访问者直接调用子系统内部模块导致的复杂引用关系,现在可以通过只访问这个统一的外观来避免;

var uav = {
    // 电子调速器
    diantiao1: {
        up() {
            console.log('电调1发送指令:电机1增大转速');
            uav.dianji1.up();
        },
        down() {
            console.log('电调1发送指令:电机1减小转速');
            uav.dianji1.down();
        }
    },
    diantiao2: {
        up() {
            console.log('电调2发送指令:电机2增大转速');
            uav.dianji2.up();
        },
        down() {
            console.log('电调2发送指令:电机2减小转速');
            uav.dianji2.down();
        }
    },
    diantiao3: {
        up() {
            console.log('电调3发送指令:电机3增大转速');
            uav.dianji3.up();
        },
        down() {
            console.log('电调3发送指令:电机3减小转速');
            uav.dianji3.down();
        }
    },
    diantiao4: {
        up() {
            console.log('电调4发送指令:电机4增大转速');
            uav.dianji4.up();
        },
        down() {
            console.log('电调4发送指令:电机4减小转速')
            uav.dianji4.down()
        }
    },
 
    // 电机
    dianji1: {
        up() { console.log('电机1增大转速') },
        down() { console.log('电机1减小转速') }
    },
    dianji2: {
        up() { console.log('电机2增大转速') },
        down() { console.log('电机2减小转速') }
    },
    dianji3: {
        up() { console.log('电机3增大转速') },
        down() { console.log('电机3减小转速') }
    },
    dianji4: {
        up() { console.log('电机4增大转速') },
        down() { console.log('电机4减小转速') }
    },
 
    // 遥控器
    controller: {
        // 上升
        up() {
            uav.diantiao1.up();
            uav.diantiao2.up();
            uav.diantiao3.up();
            uav.diantiao4.up();
        },
 
        // 前进
        forward() {
            uav.diantiao1.down();
            uav.diantiao2.down();
            uav.diantiao3.up();
            uav.diantiao4.up();
        },
 
        // 下降
        down() {
            uav.diantiao1.down();
            uav.diantiao2.down();
            uav.diantiao3.down();
            uav.diantiao4.down();
        },
 
        // 左转
        left() {
            uav.diantiao1.up();
            uav.diantiao2.down();
            uav.diantiao3.up();
            uav.diantiao4.down();
        }
    }
};
 
// 操纵无人机
uav.controller.down();    // 发送下降指令
uav.controller.left();    // 发送左转指令

4、享元模式

定义

享元(flyweight)模式是一种用于性能优化的模式,它的目标是尽量减少共享对象的数量

核心

运用共享技术来有效支持大量细粒度的对象。

强调将对象的属性划分为内部状态(属性)与外部状态(属性)。内部状态用于对象的共享,通常不变;而外部状态则剥离开来,由具体的场景决定。

实现

在程序中使用了大量的相似对象时,可以利用享元模式来优化,减少对象的数量

举个栗子,要对某个班进行身体素质测量,仅测量身高体重来评判

非亨元模式的实现:

// 健康测量
function Fitness(name, sex, age, height, weight) {
    this.name = name;
    this.sex = sex;
    this.age = age;
    this.height = height;
    this.weight = weight;
}

// 开始评判
Fitness.prototype.judge = function() {
    var ret = this.name + ': ';

    if (this.sex === 'male') {
        ret += this.judgeMale();
    } else {
        ret += this.judgeFemale();
    }

    console.log(ret);
};

// 男性评判规则
Fitness.prototype.judgeMale = function() {
    var ratio = this.height / this.weight;

    return this.age > 20 ? (ratio > 3.5) : (ratio > 2.8);
};

// 女性评判规则
Fitness.prototype.judgeFemale = function() {
    var ratio = this.height / this.weight;
    
    return this.age > 20 ? (ratio > 4) : (ratio > 3);
};


var a = new Fitness('A', 'male', 18, 160, 80);
var b = new Fitness('B', 'male', 21, 180, 70);
var c = new Fitness('C', 'female', 28, 160, 80);
var d = new Fitness('D', 'male', 18, 170, 60);
var e = new Fitness('E', 'female', 18, 160, 40);

// 开始评判
a.judge(); // A: false
b.judge(); // B: false
c.judge(); // C: false
d.judge(); // D: true
e.judge(); // E: true

评判五个人就需要创建五个对象,一个班就几十个对象

可以将对象的公共部分(内部状态)抽离出来,与外部状态独立。将性别看做内部状态即可,其他属性都属于外部状态。

这么一来我们只需要维护男和女两个对象(使用factory对象),而其他变化的部分则在外部维护(使用manager对象)

// 少数共享对象(如男女), 加载外部多数数据(如给一个班级50个人的50份数据)

// 健康测量
function Fitness(sex) {
    this.sex = sex;
}

// 开始评判
Fitness.prototype.judge = function(name) {
    // 操作前先更新当前状态(从外部状态管理器中获取)
    FitnessManager.updateFitnessData(name, this);

    var ret = name + ': ';

    if (this.sex === 'male') {
        ret += this.judgeMale();
    } else {
        ret += this.judgeFemale();
    }

    console.log(ret);
};

// 男性评判规则
Fitness.prototype.judgeMale = function() {
    var ratio = this.height / this.weight;

    return this.age > 20 ? (ratio > 3.5) : (ratio > 2.8);
};

// 女性评判规则
Fitness.prototype.judgeFemale = function() {
    var ratio = this.height / this.weight;
    
    return this.age > 20 ? (ratio > 4) : (ratio > 3);
};


// 工厂,创建可共享的对象 本例子是性别 男、女
var FitnessFactory = {
    objs: {},

    create: function(sex) {
        //单例模式
        if (!this.objs[sex]) {
            this.objs[sex] = new Fitness(sex);
        }

        return this.objs[sex];
    }
};

// 管理器,管理非共享的部分
var FitnessManager = {
    fitnessData: {},
    
    // 添加一项
    add: function(name, sex, age, height, weight) {
        var fitness = FitnessFactory.create(sex);
        
        // 存储变化的数据
        this.fitnessData[name] = {
            age: age,
            height: height,
            weight: weight
        };

        return fitness;
    },
    
    // 从存储的数据中获取,更新至当前正在使用的对象
    updateFitnessData: function(name, obj) {
        var fitnessData = this.fitnessData[name];

        for (var item in fitnessData) {
            if (fitnessData.hasOwnProperty(item)) {
                obj[item] = fitnessData[item];
            }
        }
    }
};



var a = FitnessManager.add('A', 'male', 18, 160, 80);
var b = FitnessManager.add('B', 'male', 21, 180, 70);
var c = FitnessManager.add('C', 'female', 28, 160, 80);
var d = FitnessManager.add('D', 'male', 18, 170, 60);
var e = FitnessManager.add('E', 'female', 18, 160, 40);

// 开始评判
a.judge('A'); // A: false
b.judge('B'); // B: false
c.judge('C'); // C: false
d.judge('D'); // D: true
e.judge('E'); // E: true

不过代码可能更复杂了,这个例子可能还不够充分,只是展示了享元模式如何实现,它节省了多个相似的对象,但多了一些操作。

5、状态模式

1. 定义

事物内部状态的改变往往会带来事物的行为改变。在处理的时候,将这个处理委托给当前的状态对象即可,该状态对象会负责渲染它自身的行为

2. 核心

区分事物内部的状态,把事物的每种状态都封装成单独的类,跟此种状态有关的行为都被封装在这个类的内部

3. 实现

以一个人的工作状态作为例子,在刚醒、精神、疲倦几个状态中切换着

// 工作状态
function Work(name) {
    this.name = name;
    this.currentState = null;

    // 工作状态,保存为对应状态对象
    this.wakeUpState = new WakeUpState(this);
    // 精神饱满
    this.energeticState = new EnergeticState(this);
    // 疲倦
    this.tiredState = new TiredState(this);

    this.init();
}

Work.prototype.init = function() {
    this.currentState = this.wakeUpState;
    
    // 点击事件,用于触发更新状态
    document.body.onclick = () => {
        this.currentState.behaviour();
    };
};

// 更新工作状态
Work.prototype.setState = function(state) {
    this.currentState = state;
}

// 刚醒
function WakeUpState(work) {
    this.work = work;
}


// 刚醒的行为
WakeUpState.prototype.behaviour = function() {
    console.log(this.work.name, ':', '刚醒呢,睡个懒觉先');
    
    // 只睡了2秒钟懒觉就精神了..
    setTimeout(() => {
        this.work.setState(this.work.energeticState);
    }, 2 * 1000);
}

// 精神饱满
function EnergeticState(work) {
    this.work = work;
}

EnergeticState.prototype.behaviour = function() {
    console.log(this.work.name, ':', '超级精神的');
    
    // 才精神1秒钟就发困了
    setTimeout(() => {
        this.work.setState(this.work.tiredState);
    }, 1000);
};

// 疲倦
function TiredState(work) {
    this.work = work;
}

TiredState.prototype.behaviour = function() {
    console.log(this.work.name, ':', '怎么肥事,好困');
    
    // 不知不觉,又变成了刚醒着的状态... 不断循环呀
    setTimeout(() => {
        this.work.setState(this.work.wakeUpState);
    }, 1000);
};


var work = new Work('曹操');

执行work.behaviour() , 触发更新状态的操作

6、适配器模式

定义

是解决两个软件实体间的接口不兼容的问题,对不兼容的部分进行适配

核心

解决两个已有接口之间不匹配的问题

实现

比如一个简单的数据格式转换的适配器


// 渲染数据,格式限制为数组了
function renderData(data) {
    data.forEach(function(item) {
        console.log(item);
    });
}

// 对非数组的进行转换适配
function arrayAdapter(data) {
    if (typeof data !== 'object') {
        return [];
    }

    if (Object.prototype.toString.call(data) === '[object Array]') {
        return data;
    }

    var temp = [];

    for (var item in data) {
        if (data.hasOwnProperty(item)) {
            temp.push(data[item]);
        }
    }

    return temp;
}

var data = {
    0: 'A',
    1: 'B',
    2: 'C'
};

renderData(arrayAdapter(data)); // A B C

7、装饰者模式

定义

以动态地给某个对象添加一些额外的职责,而不会影响从这个类中派生的其他对象。

是一种“即用即付”的方式,能够在不改变对 象自身的基础上,在程序运行期间给对象动态地 添加职责

核心

是为对象动态加入行为,经过多重包装,可以形成一条装饰链

实现

最简单的装饰者,就是重写对象的属性

var A = {
    score: 10
};

A.score = '分数:' + A.score;
function Person() {}

Person.prototype.skill = function() {
    console.log('数学');
};

// 装饰器,还会音乐
function MusicDecorator(person) {
    this.person = person;
}

MusicDecorator.prototype.skill = function() {
    this.person.skill();
    console.log('音乐');
};

// 装饰器,还会跑步
function RunDecorator(person) {
    this.person = person;
}

RunDecorator.prototype.skill = function() {
    this.person.skill();
    console.log('跑步');
};

var person = new Person();

// 装饰一下
var person1 = new MusicDecorator(person);
person1 = new RunDecorator(person1);

person.skill(); // 数学
person1.skill(); // 数学 音乐 跑步

在JS中,函数为一等对象,所以我们也可以使用更通用的装饰函数

// 装饰器,在当前函数执行前先执行另一个函数
function decoratorBefore(fn, beforeFn) {
    return function() {
        var ret = beforeFn.apply(this, arguments);
        
        // 在前一个函数中判断,不需要执行当前函数
        if (ret !== false) {
            fn.apply(this, arguments);
        }
    };
}


function skill() {
    console.log('数学');
}

function skillMusic() {
    console.log('音乐');
}

function skillRun() {
    console.log('跑步');
}

var skillDecorator = decoratorBefore(skill, skillMusic);
skillDecorator = decoratorBefore(skillDecorator, skillRun);

skillDecorator(); // 跑步 音乐 数学

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值