本次分享主题是依赖注入与控制反转。在进入主题前我们可以先复习一下 Robert C. Martin 总结出的面向对象设计的 SOLID 五条设计原则。 这五条原则具有很好的建设性,一般遵循这五条原则进行面向对象编程设计迭代项目,可以尽可能的避免代码出現坏味道,提升代码的质量和可维护性。
There should never be more than one reason for a class to change.
单一职责指出:一个类的的实现最好只做一件事,换句话说就是需要保证每一个类只负责一类事。
举个例子:有 Square 和 Circle 两个类,我们提供一个计算面积和输出结果的 AreaCalculator 类
class Square {
constructor(public length: number) { }
}
class Circle {
constructor(public radius: number) { }
}
class AreaCalculator {
constructor(protected shapes: (Square|Circle)[]) { }
public sum() {
return this.shapes.reduce((sum,shape: Square|Circle,index: number) => {
if(shape instanceof Square) {
return sum+=Math.pow(shape.length,2);
}
if(shape instanceof Circle) {
return sum+=Math.PI*Math.pow(shape.radius,2);
}
return sum;
},0)
}
public output() {
console.log('Sum of the areas of provided shapes: '+this.sum())
}
}
// 使用:
const shapes=[
new Circle(2),
new Square(5),
new Square(6),
];
const areas=new AreaCalculator(shapes);
areas.output();
// 上面的实现的 AreaCalculator 并未遵循 单一职责原则。 (即实现了计算面积的方法,也实现了输出方法)
// 假如我们希望 output 方法能打印出结构化数据,而不止打印出字符串。
// 我们可以做如下改造:
// 把 output 抽象成一个 SumCalculatorOutputter 类
class SumCalculatorOutputter {
constructor(protected calculator: AreaCalculator) { }
public toJSON() {
const data={sum: this.calculator.sum()};
console.log(JSON.stringify(data));
}
public toString() {
console.log('Sum of the areas of provided shapes: '+this.calculator.sum())
}
}
// 使用:
const shapes = [
new Circle(2),
new Square(5),
new Square(6),
];
const areas = new AreaCalculator(shapes);
const output = new SumCalculatorOutputter(areas);
output.toJSON();
output.toString();
Objects or entities should be open for extension but closed for modification.
开闭原则指出:在面向对象设计结构的时候,保证结构具有可扩展性而无需对其内部进行修改。
回顾之前的 AreaCalculator 类,当我们需要增加新的形状,例如 三角形或者长方形。则需要对 sum 方法进行改写。这违背了开闭原则。
class AreaCalculator {
public constructor(protected shapes: (Square|Circle)[]) { }
public sum() {
return this.shapes.reduce((sum,shape: Square|Circle,index: number) => {
if(shape instanceof Square) {
return sum+=Math.pow(shape.length,2);
}
if(shape instanceof Circle) {
return sum+=Math.PI*Math.pow(shape.radius,2);
}
return sum;
},0)
}
}
// 我们可以考虑把 sum 方法中的 caculator area 方法分散到每一个 shape 类中。
interface IShape{
area():number;
}
class Square implements IShape{
constructor(public length: number) { }
public area() {
return Math.pow(this->length, 2);
}
}
class Circle implements IShape {
constructor(public radius: number) { }
public area() {
return Math.PI*Math.pow(this.radius,2);
}
}
class AreaCalculator {
public constructor(protected shapes: IShape[]) { }
public sum() {
return this.shapes.reduce((sum,shape: IShape,index: number) => (sum+shape.area()),0)
}
}
// 我们还可以添加 Rectangle 类
class Rectangle implements IShape {
constructor(public width: number,public height: number) { }
public area() {
return this.width*this.height;
}
}