坚固。2021年它还有用吗?
何必呢?
在软件开发领域,存在两个极端。
- 不遵循最佳实践的人。
- 追随他们走向极端的人。
如果您像我一样懒惰,那么您大多不会遵循最佳实践,因为YAGNI(您不需要它),但如果您像我一样,那么您大多会遵循最佳实践,例如SOLID 设计原则。
等等。为什么我同时站在两边?因为我会根据自己的情况遵循这两种做法。如果事情简单、范围有限且可预测,那谁还需要过度思考最佳实践?但如果事情复杂、可能变得复杂,需要可扩展且可维护,那么我们确实需要最佳实践。
如果您正在构建一个将来需要更改的系统,那么您会很高兴看到SOLID让您的生活变得轻松。
什么是 SOLID?
SOLID 是 5 条原则的缩写
它们旨在使您的代码易于管理、可维护、可扩展并带来其他好处。
笔记
它们不是规则,而是最佳实践。
SOLID 背后的人
这是在 2000 年。Robert C. Martin在他的论文《设计原则和设计模式》中首次将 SOLID 作为不同设计原则的子集进行介绍。
设计原则和模式是不同的,SOLID是原则。
那么这些原则意味着什么?
每个 SOLID 原则都旨在通过遵循一定的规则来实现一定的目标。
1.单一职责原则
它旨在分离行为或关注点。这意味着每段代码都应该有一个特定的存在目的,并且只能用于该目的。
例子
以下函数仅应根据给定的 ID 来验证用户。
function validateUser(userId){
// will validate user with their userId
}
如需完整参考,请详细查看单一职责原则。
2. 开闭原则
我们的目标是避免修改某个模块的代码片段也需要更新所有依赖模块的情况。基本上,我们不允许新代码修改旧代码。
我们可以扩展代码,但不能修改它。一个实际用例是那些具有向后兼容性的软件。
例子
一个 TypeScript 示例
interface PaymentMethod {
pay() : boolean
}
class Cash implements PaymentMethod {
public pay(){
// handle cash pay logic here
}
}
function makePayment(payMethod: PaymentMethod) {
if(payMethod.pay()){
return true;
}
return false;
}
在上面的代码中,如果我们想添加信用卡付款,我们所要做的就是添加以下代码(以及实际的实现),它就可以正常工作
class CreditCard implements PaymentMethod {
public pay(){
// handle credit pay logic here
}
}
如需完整参考,请查看我的另一篇有关开放/关闭原则的文章。
3.里氏替换原则
该原则告诉我们,如果我们用父类替换子类的实例,我们的代码仍然可以正常工作,而不会中断或产生副作用。
例子
class Printer{
function changeSettings(){
// common functionality
}
function print(){
// common functionality
}
}
class LaserPrinter extends Printer{
function changeSettings(){
// ... Laser Printer specific code
}
function print(){
// ... Laser Printer specific code
}
}
class _3DPrinter extends Printer{
function changeSettings(){
// ... 3D printer specific code
}
function print(){
// ... 3D printer specific code
}
}
然而,该原则也有其局限性,我在其另一篇文章中对此进行了讨论。请参阅里氏替换原则,了解其局限性的示例。
4.接口隔离原则
该原则旨在使用角色接口(或一般的角色模块),这些接口是为特定目的而设计的,并且只能用于这些目的。它说
客户端不应该被迫依赖他们不使用的接口。
该原则解决了接口隔离原则的一些问题,例如我在关于里氏替换原则的文章中提到的 Bird 示例
例子
这是一个打字稿示例,但仍然不太难理解。
interface BirdFly{
fly(): void;
}
interface BirdWalk{
walk(): void;
}
class Duck implement BirdFly, BirdWalk{
fly(){
// Duck can fly
}
walk(){
// Duck can walk
}
}
class Ostrich implement BirdWalk{
walk(){
// Ostrich can walk
}
}
如需完整参考,请详细查看接口隔离原则。
5.依赖倒置原则
它专注于使用抽象或外观/包装模式来隐藏低级模块的细节,使其不被高级实现所了解。
我们基本上创建了位于高级模块和低级模块之间的包装类。如果低级实现彼此不同,这将非常有帮助。
例子
再次使用 TypeScript 示例
interface Payment {
pay(): boolean
}
// (Wrapper/Abstraction around cash payment)
class CashHandler implements Payment {
constructor(user){
this.user = user
this.CashPayment = new CashPayment();
}
pay(amount){
this.CashPayment.pay(amount)
}
}
// (low-level module)
class CashPayment {
public pay(amount){
// handle cash payment logic
}
}
// (High-level Module)
function makePayment(amount: number, paymentMethod: Payment){
if(paymentMethod.pay(amount)){
return true;
}
return false;
}
如需完整参考,请详细查看依赖倒置原则。
何时使用什么以及避免什么
现在我们对每个原则有了简单的了解,我们将研究何时使用和避免它们。
使用 | 避免 | |
---|---|---|
单一职责 | 用于可扩展和可维护的代码。 | 当出现过多的碎片且未来变化不可预测时。 |
打开 关闭 | 防止旧代码因新代码而破坏。 | 当过度设计时。 |
利斯科夫替换 | 父/子可互换使用而不会中断。 | 当替换没有意义时。(以鸟为例) |
接口隔离 | 对于角色特定的接口。 | 当由于模块太多而难以进行聚合和隔离时。 |
依赖倒置 | 针对不同的低级实现。 | 当不需要低级模块的不同实现时,例如大多数语言中的 String 类就不会改变,因为大多数情况下不需要。 |
这些主要是原因,你可以不同意,但这一切都取决于你所处理的事情。
SOLID 在 2021 年仍然有用吗?
问问你自己,是否存在一种语言,可以用一行代码完成所有事情?
do_everything();
我想不是,除非你或其他人创造了一种比 Python 使用更少代码的语言,并且用一行代码完成所有事情,否则你确实需要SOLID设计原则。
当然,存在极端情况和无法实现SOLID 的情况,但如果您觉得舒服并且可以使用 SOLID,那么您应该这样做。
结论
那么,你对此有何看法?你是否也采取了和我类似的方法?如果你喜欢这篇文章,请务必点个💖。
文章来源:https://dev.to/rhuzaifa/solid-is-it-still-useful-in-2021-5ff6