okoala / blog Goto Github PK
View Code? Open in Web Editor NEW我的博客
我的博客
todo ...
SOLID 是由 Robert C. Martin,twitter账号:@UncleBob 提出的 面向对象编程 和 面向对象设计 的五个基本原则。刚开始 Uncle Bob 提出时候并没有使用 SOLID 这个名称,直到 Michael Feathers 出现。SOLID 被典型的应用在测试驱动开发上,并且是敏捷开发以及自适应软件开发的基本原则的重要组成部分。
这5大原则对于开发者来说是非常重要的,是大家必须要掌握的知识点。明白和应用这些原则会促使你写出更高质量的代码,进而成为一位更优秀的开发者。
应用它对代码来说至少有三个好处:
去构建一个好的应用,我们必须去掌握两点:低耦合和高内聚
那什么是耦合?
那什么是内聚?
而 **SOLID ** 原则可以帮助我们去完成这个任务。使我们的应用更健壮、更具
缩写 | 名称 | 概念 |
---|---|---|
SRP | 单一责任原则 | 一个对象应该仅具有一种单一功能的概念 |
OCP | 开放封闭原则 | 实体应该是对于扩展开放,对于修改封闭的 |
LSP | 里氏替换原则 | 对象应该是可以在不改变程序正确性的前提下被它的子类所替换 |
ISP | 接口分离原则 | 多个特定的接口要好于一个宽泛用途的接口 |
DIP | 依赖倒置原则 | 一个方法应该依赖于抽象而不是实例 |
一个类只有一个会导致其变更的原因
意味着一个类有且仅有一个职责。否则,假如一个类超过了一个职责,那将会是我们的代码耦合性更高,在变更的时候就会更容易出错。
class Customer {
private name: String;
// 各种getter 和 setter方法
// 这是一个职责
public storeCustomer(customerName: String): void {
// 保存customer到数据库
}
// 这是另一个职责
public generateCustomerReport(customerName: String): void {
// 生成报告
}
}
storeCustomer 有一个存储customer到数据库的职责,它是一个持久化的职责。所以我们需要把它移出Customer类
generateCustomerReport 有一个生成报表的职责,所以我们也需要把它移出Customer类
我们需要给不同的职责创建不同的类
class Customer {
private name: String;
// 各种 getter 和 setter
}
class CustomerDB {
public storeCustomer(customerName: String): void {
// 存储用户数据进数据库中
}
}
class CustomerReportGenerator {
public generateReport(customerName: String): void {
// 生成报告
}
}
实践 SIP,让每个类都只拥有单一职责,让我们的代码拥有低耦合,高内聚
一个软件实体(类, 模块,函数)应该对扩展开发,对修改关闭
依据这个原则,一个软件实体必须是易于扩展新特性的,且不会改变已经存在的代码
对扩展开放: 可以添加新的行为去满足新的需求
对修改关闭: 去扩展新的行为不能去修改已经存在的代码
如果我们应用了这原则,我们会获得一个具有扩展性的系统,而且即使需求改变了,我们的系统也不容易错误。我们可以使用 抽象 和 多态 来应用它
class Rectange {
private width: Number;
private height: Number;
// 一堆 getter 和 setter 方法
}
class Square {
private side: Number;
// 一堆 getter 和 setter 方法
}
class ShapePrinter {
public drawShape(shape: Object) {
if (shape instanceof Rectangle) {
// Draw Rectangle
} else if (shape instanceof Square) {
// Draw Square
}
}
}
我们发现,每次要添加新的图形都要修改drawShape方法,所以 ShapePrinter 类并没有对修改关闭
interface Shape {
draw(): void;
}
class Renctange implements Shape {
private width: Number;
private height: Number;
// getter setter
public draw(): void {
// D
}
}
class Square implements Shape {
private side: Number;
public draw(): void {
// d
}
}
class ShapePrinter {
public drawShape(shape: Shape) {
shape.draw();
}
}
之后我们要添加一个新的图形,只要实现 Shape 就可以了,ShapePrinter 类完全不需要动。
可以这么说,好的软件架构一定遵循开闭原则的,我们在应用23种设计模式的时候其实目的也是遵循开闭原则。所以好的抽象对保持软件架构的稳定至关重要,软件细节用实现类进行扩展。当软件发生改变的时候我们只需要重新派生出一个实现类就可以了。总的来说就是 用抽象构建框架,用实现扩展细节
将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常
通过应用此原则,我们可以验证我们的抽象是否是正确的
class Rectangle {
private width: Number;
private height: Number;
public setWidth(width: Number): void {
this.width = width;
}
public setHeight(height: Number): void {
this.height= height;
}
public getArea(): Number {
return <number>this.width * <number>this.height;
}
}
按照数学上来说,正方形也是长方形中的一种,所以我们把 Square 设为 Rectangle 的子类
class Square extends Rectangle {
public setWidth(width: Number) {
super.setWidth(width);
super.setHeight(width);
}
public setHeight(height: Number) {
super.setWidth(height);
super.setHeight(height);
}
}
看这代码,我们发现一个问题,重写 Rectangle 基类之后,setWidth,setHeight本身的行为被改变了。
const rectangle: Rectangle = new Square();
rectangle.setWidth(2);
rectangle.setHeight(5);
// 25
在实际编程中,我们常常会通过重写父类的方法来完成新功能,这样写虽然简单,但是整个继承体系的可复用性会很差。如果还运用多态,那出错的概率就很大了。
一般我们是采用 Abstract Class 或者 Interface 的做法来解决这个问题。
根据这里的情况,我们使用 Interface。
interface Shape {
area(): Number;
}
class Rectangle implements Shape {
private width: Number;
private height: Number;
public setWidth(width: Number): void {
this.width = width;
}
public setHeight(height: Number): void {
this.height = height;
}
public area(): Number {
return <number>this.width * <number>this.height;
}
}
class Square implements Shape {
private size: Number;
public setSize(size: Number): void {
this.size = size;
}
public area() {
return <number>this.size * <number>this.size;
}
}
当然,除此之外还有其他的方案。
子类可以扩展父类的功能,但不能改变父类原有的功能
一个类不应该依赖它不需要的接口,应该是建立在最小的接口上
所以实现具体的接口比实现通用的接口更好。
SOLID (面向对象设计)
SOLID Development Principles – In Motivational Pictures
SOLID principles using Typescript
SOLID Principles : The Definitive Guide
设计模式六大原则(6):开闭原则
传统React的栈情况:
问题存在着:
// 错误的
this.setState({
counter: this.state.counter + this.props.increment
});
正确的姿势:
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));
待定
Clean Code JavaScript
从达标到卓越 —— API 设计之道
Animated Intro RxJS
线性代数为我们提供了一套便捷的概念和语言来讨论 “空间”。
对于大部分的东西来说,如果将其放大,那么几乎都是平直的,所以只要小范围内进行考察,通过用 “平直的东西” 来进行近似,也是可以得到很有用的结果的。平面也是类似。
我们几乎处处都会遇到要处理由多个数值组成的成组的数据的情况。对于这样的数据,我们不是简单的将其看成一组组的数,而是看作 “空间的点” 来进行直观的处理。
名称 | 字面意思 | 实际意思 |
---|---|---|
向量 | 排成一列的数字 | 有向线段、空间内的点 |
矩阵 | 排成矩阵的数字 | 空间到空间的映射 |
行列式 | 麻烦的计算 | 上面的映射对应的 “体积扩大率” |
矩阵满足结合律,但是不满足交换律
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.