设计模式有很多,这些设计模式的出现是由准则或者说有根据的,不是平白无故出现的,下面我们就来认识一下这些设计原则。
在此以前,我想说一下,看这篇文章的读者应该都是前端工程师或者使用JS编程语言的程序员,为了结合JS语言的特性(弱类型,无接口等)有一些原则只是简单的说一下,在JS中基本不会使用,只需要你了解。
如果想要全面学习设计原则以及23种设计模式,应该选择Java而不是JS。我们只针对JS语言和前端过程中可以使用到的一些设计原则。
1 单一职责原则(S):一个程序只做一件事,如果事情过于复杂,那么就应该拆开,使得每一部分保持独立。
2 开发封闭原则(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);
看一下效果如何
预期效果一样。
推荐大家自己动手实现,自己写出来了,才学会了。
大家可以看左边个人分类的目录,跟着一起学习。