JavaScript -- 设计模式 设计原则

设计模式有很多,这些设计模式的出现是由准则或者说有根据的,不是平白无故出现的,下面我们就来认识一下这些设计原则。

在此以前,我想说一下,看这篇文章的读者应该都是前端工程师或者使用JS编程语言的程序员,为了结合JS语言的特性(弱类型,无接口等)有一些原则只是简单的说一下,在JS中基本不会使用,只需要你了解。

如果想要全面学习设计原则以及23种设计模式,应该选择Java而不是JS。我们只针对JS语言和前端过程中可以使用到的一些设计原则。

1  单一职责原则(S):一个程序只做一件事,如果事情过于复杂,那么就应该拆开,使得每一部分保持独立。

开发封闭原则(0):对修改封闭,对扩展开发。在遇到新的需求增加时,应该扩展新代码而不是修改原来的代码。

3  里氏代换原则(L):父类出现的地方子类也可以,也就是子类可以替换父类而且使得程序可以正常运行。

4  接口隔离原则(I):接口的设计应该保持单一功能,使用多个单一功能的接口比使用一个总接口要好。

5  依赖倒转原则(d):抽象不应该依赖于细节(实现类),细节应该依赖于抽象。高层模板不应该依赖于低层模块,应该依赖于抽象。

6  迪米特原则原则(最少知道原则):一个类对另外一个类应该了解越少越好,高内聚,低耦合。

回归上面提到的,我们是前端工程师,这后面四种作为了解内容,但是前面俩种必须知道,这是我们的重点内容。

 

单一职责和开发封闭是我们应该需要掌握的内容,回想一下自己以前写的代码是否符合这俩个准则。

举一个简单的例子来熟悉一下这俩个准则。


function loadImg(src) {
    return new Promise((resolve, reject) => {
        const img = document.createElement("img");
        img.src = src;
        img.onload = function () {
            resolve(img);
        }
        img.onerror = function () {
            reject("图片加载错误!")
        }
    })
}

let src = "http://img0.imgtn.bdimg.com/it/u=276772730,3789231562&fm=26&gp=0.jpg";
const result = loadImg(src);
result.then((img) => {
    console.log(`${img.width}`)
    return img;
}).then((img) => {
    console.log(`${img.height}`)
    return img
}).catch((err) => {
    console.log(err);
})

这是一个使用promise来加载图片的例子。

这个例子里面我使用了俩个then第一个then里面打印的是img.width  第二个then里面是打印img.height。虽然看起来是很傻的俩个操作,但是我想强调的是,每一个then就相当于一个程序段,里面只需要完成自己单一的任务,当增加其他功能时,我们不需要修改上面的俩个then,再添加一个then即可。

假如我们要进行的操作是否复杂,而且十分庞大,全部都放在一个程序段里面完成,那么在后期维护上面就十分的复杂了。上面的例子也许不是很恰当,但是能够表示出意思。

单一职责和开放封闭是我们学习的重点法则。

 

接下来 我们看俩道题目,来帮助大家理解面向对象设计原则。 

 

大家觉得这题目如何,假如需要你设计你该如何设计了?思考一下,然后在继续看下面内容。

首先,我们可以知道任何车都有车牌号和名称,也就是说我们可以抽象一个车作为父类,包含车牌号和名称。因为价格,快车和专车不一样,我们可以把价格属性放到子类(快车和专车)中。

大家现在想一下,行程和是什么车有关系吗?快车有行程,专车也有行程,即行程应该和父类关联起来,而不是子类。最后一点,打车金额是行程的还是车的?  很明显应该是行程的。

画出UML图

写出JS代码

class Car {
    constructor(name, number) {
        this.name = name;
        this.number= number;
    }
}

class KuaiChe extends Car {
    constructor(name, number) {
        super(name, number);
        this.price = 1;
    }
}
class ZhuanChe extends Car {
    constructor(name, number) {
        super(name, number);
        this.price = 2;
    }
}
class Trip {
    constructor(c) {
        this.car = c;
    }
    start() {
        console.log(`${this.car.name}车为你服务,车牌号为${this.car.number}`)
    }
    end() {
        console.log(`你需要支付${this.car.price * 5}元`)
    }
}

const c = new KuaiChe("奔驰", 100);
const t = new Trip(c);
t.start();
t.end();

 

下面题目会有难度

这道题目相较于上面一道题目难度提升很大。

从第一句话就应该了解到,我们需要建立停车场类(park) 层类(floor) 停车位类(place)

第二句话,我可以得到每个车位应该有俩个方法(in out)来表示车的驶入和离开,还应该在停车位类上面设置一个标志位isEmpty表示是否是空的。

第三句话:我们应该知道停车场里面有一个方法用于计算机每一层的空位,那么计算每一层的空位应该谁来做比较好?很明显应该是floor类来做。

第四句话:我们应该知道还需要一个摄像头类,当车辆进入后,用来记录车牌号和时间,记录的值应该保存在停车场里面。

第五句话:我们应该需要一个显示器类,当车辆出来时,显示车牌号和停车时长。

UML类图如下:

我们用代码实现一下

class Car { //车类
    constructor(number) {
        this.number = number; //车牌号
    }
}

class Camera {//照相机类
    constructor() {};
    shot(car) { 
        return {
            number: car.number,
            cTime: Date.now()
        }
    }
}
class Screen { //显示器
    constructor() {};
    show(obj) {
        console.log(`车牌号${obj.number}准备离开,停车时间${Date.now()-obj.cTime}`)
    }
}

class Place { // 车位
    constructor() {
        this.isEmpty = true;
    }
    in() { //有车进入
        this.isEmpty = false;
    }
    out() { //有车出去
        this.isEmpty = true;
    }
}

class Floor { // 层类
    constructor(index, places) {
        this.index = index;
        this.places = places;
    }
    calEmptyPlace() { //计算每一层空位
        let cnt = 0;
        this.places.forEach((item) => {
            if (item.isEmpty) cnt++;
        })
        return cnt;
    }
}
class Park { //停车场
    constructor(floors) {
        this.floors = floors;
        this.camera = new Camera();
        this.screen = new Screen();
        this.car = {};
    }
    calEmpty() {
        this.floors.forEach((item) => {
            console.log(`第${item.index}层的空位是${item.calEmptyPlace()}个\n`)
        })
    }
    in(car) {
        this.calEmpty(); //显示有多少车辆

        const info = this.camera.shot(car); //拍照
        //停在那里 这个由车主自己决定
        const index = 0; //假设第一层
        const pot = parseInt((Math.random() * 100) % 100);
        const place = this.floors[index].places[pot]; //取车位
       
        place.in(); //进入

        this.car[info.number] = {
            info,
            place
        }//保存拍照信息
    }
    out(car) {
        this.screen.show(this.car[car.number].info); //显示
        this.car[car.number].place.out();
    }
}

const floors = [];
for (let i=0; i<3; i++) {
    let places = [];
    for (let j=1; j<=100; j++) {
        places[j] = new Place();
    }
    floors[i] = new Floor(i+1, places);
}

const p = new Park(floors);

const car1 = new Car(1);
const car2 = new Car(2);
const car3 = new Car(3);

console.log("第一辆车来了")
p.in(car1);
console.log("第二辆车来了")
p.in(car2);
console.log("第一辆车走了")
p.out(car1);
console.log("第二辆车走了")
p.out(car2);
console.log("第三辆车来了")
p.in(car3);
console.log("第三辆车走了")
p.out(car3);

看一下效果如何

预期效果一样。

推荐大家自己动手实现,自己写出来了,才学会了。

大家可以看左边个人分类的目录,跟着一起学习。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值