Kotlin SOLID 原则

news/2025/2/12 7:57:54/

Kotlin SOLID 原则

Kotlin SOLID 原则

许多 Kotlin 开发者并不完全了解 SOLID 原理,即使他们知道,他们也不知道为什么要使用它。您准备好了解所有细节了吗?

介绍

亲爱的 Kotlin 爱好者,您好!欢迎来到我的新文章。今天我要讲的是 Kotlin 中的 SOLID 原则。首先,我将通过示例来解释什么是 SOLID 原则以及它们的用途。

什么是 SOLID 原则?

SOLID 是帮助创建可维护、可扩展和健壮软件的五个设计原则的首字母缩写词。Robert C. Martin 介绍了这些原则来指导程序员编写高质量的代码。虽然最初用于面向对象编程,但 SOLID 也适用于 Kotlin 等其他语言。这些原则旨在促进干净的代码和改进软件设计。SOLID原则如下:

  • 单一职责原则
  • 开闭原则
  • 里氏替换原则
  • 接口隔离原则
  • 依赖倒置原则
    现在,如果您准备好了,让我们通过正确使用和违反的示例来详细了解这些原则。

单一职责原则

单一职责原则(SRP)是面向对象编程中SOLID编程原则的一部分。它表示一个特定的类应该只有一个改变的目的。**这意味着一个类应该只有一个责任或工作。SRP通过保持类和函数的组织性和易于理解来维护它们很有用。**当一个类具有多重职责时,这些职责可能会无意中影响该类的其他任务或工作,从而导致意外行为、错误和增加的维护成本。

下面我们来看一下违规情况及其正确用法。

违反:

// Single Responsibility Principle Violation
// In this example the System class is trying to handle many different situation at the same time. 
// This approach can cause major problems in the future.
class SystemManager {fun addUser(user: User) { }fun deleteUser(user: User) { }fun sendNotification(notification:String) {}fun sendEmail(user: User, email: String) {}
}

违反单一职责原则
在这个例子中,系统类试图在同一个地方处理许多不同的情况。这种方法可能会在未来引起重大问题。

正确用法:

// Single Responsibility Principle Correct Usage:
// As seen in this example, we divided our System class into specific parts
// And placed the functions in their respective classes.class MailManager() {fun sendEmail(user: User, email: String) {}
}class NotificationManager() {fun sendNotification(notification: String) {}
}class UserManager {fun addUser(user: User) {}fun deleteUser(user: User) {}
}

单一职责原则正确使用
如本例所示,我们将 System 类划分为特定的部分,并将函数放在它们各自的类中。

开闭原则

开放/封闭原则是面向对象设计中的一条规则,它表示类、模块、函数和其他软件实体应该对扩展开放但对修改关闭。这意味着您可以在不更改其原始代码的情况下向类中添加新内容。因此,无需更改类本身,您可以编写使用现有类添加新功能的新代码。这样做使代码更易于维护和重用。

下面我们来看一下违规情况及其正确用法。

违反:

// Open/Closed Principle Violation
// In this example, when we try to add something new to our class,
// we have to rewrite our existing code, which can cause problems later on.
class Shape(val type: String, val width: Double, val height: Double)fun calculateArea(shape: Shape): Double {if (shape.type == "rectangle") {return shape.width * shape.height} else if (shape.type == "circle") {return Math.PI * shape.width * shape.width}return 0.0
}

违反开闭原则
在此示例中,当我们尝试向类中添加新内容时,我们必须重写现有代码,这可能会在以后引起问题。

正确用法:

// Open/Closed Principle Correct Usage
// As in correct usage, instead of changing the class itself,
// we wrote new classes using our existing class 
// and implemented our functions under new classes.interface Shape {fun area(): Double
}class Rectangle(val width: Double, val height: Double) : Shape {override fun area() = width * height
}class Circle(val radius: Double) : Shape {override fun area() = Math.PI * radius * radius
}fun calculateArea(shape: Shape) = shape.area()

开闭原则正确用法
按照正确的用法,我们不改变类本身,而是使用现有类编写新类,并在新类下实现我们的功能。

里氏替换原则

Liskov 替换原则是面向对象编程中的一个重要规则。它说如果你有一个程序可以处理某种类型的对象,你应该能够毫无问题地使用该对象的任何子类型。这意味着主类中的所有方法和属性也应该适用于所有子类,而无需更改任何内容。

下面我们来看一下违规情况及其正确用法。

违反:

// Liskov Substitution Principle Violation:
// As we saw in this example, the method we wrote in our main class should work properly in its subclasses according to the Liskov principle, 
// but when our subclass inherited from our superclass, our fly method did not work as expected.open class Bird {open fun fly() {}
}class Penguin : Bird() {override fun fly() {print("Penguins can't fly!")}
}

违反里氏替换原则
正如我们在这个例子中看到的,我们在主类中编写的方法根据 Liskov 原则应该在其子类中正常工作,但是当我们的子类继承自超类时,我们的 fly 方法没有按预期工作。

正确用法:

// Liskov Substitution Principle Correct Usage
// As you can see in this example, all the things we write in the superclass will be valid in the subclasses, 
// because we have implemented the method that is not valid for subclasses by creating an interface and implementing it where we need it.open class Bird {// common bird methods and properties
}interface IFlyingBird {fun fly(): Boolean
}class Penguin : Bird() {// methods and properties specific to penguins
}class Eagle : Bird(), IFlyingBird {override fun fly(): Boolean {return true}
}

里氏替换原则的正确用法
正如你在这个例子中看到的,我们在超类中写的所有东西在子类中都将有效,因为我们通过创建一个接口并在我们需要它的地方实现该接口来实现了对子类无效的方法。

接口隔离原则

接口隔离原则是制作计算机程序的规则。它说当我们制作程序的不同部分时,我们不应该以相同的方式制作它们。相反,我们应该让它们更小、更具体,这样程序的其他部分就不必依赖于它们不需要的东西。这有助于我们编写更易于更改和维护的代码,因为每个部分只做它需要做的事情。

下面我们来看一下违规情况及其正确用法。

违反:

// Interface Segregation Principle Violation
// When we look at our example, we see that the interface we created contains many methods.
// If we do everything inside a common interface, we may have made unnecessary use in the places that implement our interface.
// Instead, we can divide our system into smaller interface parts.interface Animal {fun swim()fun fly()
}class Duck : Animal {override fun swim() {println("Duck swimming")}override fun fly() {println("Duck flying")}
}class Penguin : Animal {override fun swim() {println("Penguin swimming")}override fun fly() {throw UnsupportedOperationException("Penguin cannot fly")}
}

违反接口隔离原则
当我们查看我们的示例时,我们看到我们创建的接口包含许多方法。如果我们在一个公共接口内做所有事情,我们可能在实现接口的地方做了不必要的使用。相反,我们可以将我们的系统分成更小的接口部分。

正确用法:

// Interface Segregation Principle Correct Usage
// As we saw in the correct usage example, dividing the system into smaller interfaces and using them where we needed them made it much easier to change the system in the future.interface CanSwim {fun swim()
}interface CanFly {fun fly()
}class Duck : CanSwim, CanFly {override fun swim() {println("Duck swimming")}override fun fly() {println("Duck flying")}
}class Penguin : CanSwim {override fun swim() {println("Penguin swimming")}
}

接口隔离原则正确使用
正如我们在正确使用示例中看到的那样,将系统划分为更小的接口并在我们需要的地方使用它们使得将来更改系统变得更加容易。

依赖倒置原则

依赖倒置原则是一个 SOLID 原则,它指出高级模块不应该依赖于低级模块,但两者都应该依赖于抽象。这意味着类应该依赖于抽象,而不是具体的实现。DIP 背后的思想是将组件彼此解耦,这使得代码更加模块化、更易于测试和更易于维护。

下面我们来看一下违规情况及其正确用法。

违反:

// Dependency Inversion Principle Violation
// As we can see in this example, each of our payment methods is processed separately in our Service class in a hard code way.
// Instead of a hard code implementation, the system needed to be DEPEND to an abstract structure.class PaymentService {private val paymentProcessorPaypal = PaypalPaymentProcessor()private val paymentProcessorStripe = StripePaymentProcessor()fun processPaymentWithPaypal(amount: Double): Boolean {return paymentProcessorPaypal.processPayment(amount)}fun processPaymentWithStripe(amount: Double): Boolean {return paymentProcessorStripe.processPayment(amount)}
}class PaypalPaymentProcessor {fun processPayment(amount: Double): Boolean {// Process payment via Paypal APIreturn true}
}class StripePaymentProcessor {fun processPayment(amount: Double): Boolean {// Process payment via Stripe APIreturn true}
}fun main() {val paymentService = PaymentService()println(paymentService.processPaymentWithPaypal(50.0)) // Process payment via Paypal APIprintln(paymentService.processPaymentWithStripe(50.0)) // Process payment via Stripe API
}

违反依赖倒置原则
正如我们在这个例子中看到的,我们的每一种支付方式都以硬编码的方式在我们的服务类中单独处理。系统需要依赖于抽象结构,而不是硬代码实现。

正确用法:

// In the correct usage example, we did not have to implement hard code about our payment methods in our Service class,
// because we set up an abstract structure with the interface that we created.interface PaymentProcessor {fun processPayment(amount: Double): Boolean
}class PaypalPaymentProcessor : PaymentProcessor {override fun processPayment(amount: Double): Boolean {// Process payment via Paypal APIreturn true}
}class StripePaymentProcessor : PaymentProcessor {override fun processPayment(amount: Double): Boolean {// Process payment via Stripe APIreturn true}
}class PaymentService(private val paymentProcessor: PaymentProcessor) {fun processPayment(amount: Double): Boolean {return paymentProcessor.processPayment(amount)}
}fun main() {val paymentProcessor = PaypalPaymentProcessor()val paymentService = PaymentService(paymentProcessor)println(paymentService.processPayment(50.0)) // Process payment via Paypal API
}

在正确的用法示例中,我们不必在我们的服务类中实现关于我们的支付方法的硬代码,因为我们使用我们创建的接口设置了一个抽象结构。

结论

因此,SOLID 原则对于在 Kotlin 中创建可维护、可扩展且高效的软件至关重要。利用 Kotlin 的独特功能和构造,开发人员可以设计遵循这些准则的模块化、松散耦合的系统。坚持 SOLID 原则不仅可以提高代码的可测试性,还可以鼓励持续改进和最佳实践的文化。最终,在 Kotlin 开发中采用这些原则会产生更高质量的软件,这些软件可以得到有效维护并适应不断变化的需求。

在这里插入图片描述

参考

【kotlin语言】https://kotlinlang.org/
【Kotlin中的SOLID原则】https://proandroiddev.com/solid-design-principles-in-kotlin-79100c670df1
【SOLD原则:Kotlin之道】https://medium.com/the-android-caf%C3%A9/solid-principles-the-kotlin-way-ff717c0d60da
【SOLD原则与Kotlin示例】https://codersee.com/solid-principles-with-kotlin-examples/
【采用SOLID原则,编写干净可维护的代码】https://bootcamp.uxdesign.cc/adopting-solid-principles-for-clean-and-maintainable-code-with-kotlin-51e615c6b315


http://www.ppmy.cn/news/88030.html

相关文章

网络设备端口别名设置

网络设备端口通常使用数字编号,如FastEthernet 0/1,GigabitEthernet 1/0/1等。这些数字表示方法虽然清晰准确,但当管理大量网络设备和端口时,数字表示法的可读性会变差,并不利于记忆。 端口别名可以为端口设置一个自定义的别名,使用户更易识别和记住各端口的连接情况。主流网络…

如何在Angular应用程序中插入自定义 CSS?这里有答案!

Kendo UI for Angular是专用于Angular开发的专业级Angular组件,telerik致力于提供纯粹的高性能Angular UI组件,无需任何jQuery依赖关系。 Kendo UI R1 2023正式版下载(Q技术交流:726377843) 为什么需要在 Angular 应用程序中插入…

fastapi异步框架操作的理解

FastAPI的异步操作是指在处理请求时,使用异步代码来实现请求的处理。这种异步操作可以提高Web应用程序的性能和响应速度。异步操作使得Web应用程序可以在等待其他资源(如数据库或网络请求)响应时继续处理其他请求,从而提高了Web应…

Java时间类(十二) -- LocalDateTime类工具类 -- Java获取当天、本周、本月、本年 开始及结束时间

目录 1. 今天的日期如下: 2. LocalDateTimeUtils工具类的源代码: 3. 测试类: 1. 今天的日期如下:

信号的能量和方差和功率的关系

信号的能量和方差是两个不同的概念,它们的定义和计算方法也不同。 对于一个离散时间的信号 x [ n ] x[n] x[n],它的能量可以表示为: E x ∑ n − ∞ ∞ ∣ x [ n ] ∣ 2 E_x \sum_{n-\infty}^{\infty} |x[n]|^2 Ex​n−∞∑∞​∣x[n]∣…

Spring Boot中的AOP

Spring Boot中的AOP 前言讲讲Spring AOP是什么Aop的组成部分有哪些 各部分的作用是什么连接点和切点的关联是什么详细讲讲一个切面在目标对象创建到执行目标对象方法的过程AOP各组成部分的相关注解有哪些,其作用是什么给我一个在com.demo.controller中所有方法执行前…

吴恩达 x OpenAI Prompt Engineering教程中文笔记

Datawhale干货 作者:刘俊君,Datawhale成员 完整课程:《吴恩达ChatGPT最新课程》 🐳Reasons & Importance Important for research, discoveries, and advancement 对研究、发现和进步很重要 Accelerate the scientific resea…

Qt连接MySQL数据库(保姆级成功版教程)

一、 VIP通道可以关注我,私信我,直接给两个动态库,直接起飞。 1、安装Qt时勾选sources 2、配置path环境变量 ① 此电脑->属性->高级系统设置->环境变量 ② 双击path->右上角新建 ③把这两个路径添加进去,最后确…