Kotlin SOLID 原则

news2024/12/24 8:52:38

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 API
        return true
    }
}

class StripePaymentProcessor {
    fun processPayment(amount: Double): Boolean {
        // Process payment via Stripe API
        return true
    }
}


fun main() {
    val paymentService = PaymentService()
    println(paymentService.processPaymentWithPaypal(50.0)) // Process payment via Paypal API
    println(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 API
        return true
    }
}

class StripePaymentProcessor : PaymentProcessor {
    override fun processPayment(amount: Double): Boolean {
        // Process payment via Stripe API
        return 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.coloradmin.cn/o/570896.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Qt编程基础 | 第六章-窗体 | 6.2、VS导入资源文件

一、VS导入资源文件 1.1、导入资源文件 步骤一: 将所有图片放到各自文件夹下,并将文件夹拷贝到资源文件(.qrc文件)的同级目录下,如下: 步骤二: 新建VS项目的时候,系统会自动建好一…

如何在华为OD机试中获得满分?Java实现【最差产品奖】一文详解!

✅创作者:陈书予 🎉个人主页:陈书予的个人主页 🍁陈书予的个人社区,欢迎你的加入: 陈书予的社区 🌟专栏地址: Java华为OD机试真题(2022&2023) 文章目录 1. 题目描述2. 输入描述3. 输出描述…

NetApp AFF C 系列——可持续、可扩展且安全可靠的全闪存解决方案

NetApp AFF C 系列 采用全新的闪存技术,同时辅以智能科技加持,将为您带来一个更为经济实惠的全闪存解决方案,它重新定义了安全性、可扩展性和可持续性。 为什么选择 AFF C 系列的新一代全闪存解决方案? 实现现代化,打…

chatgpt+mj+did生成会说话的头像

chatgptmjdid生成会说话的头像 当我们有了 ChatGPT 生成的内容,有了 stable-diffusion、midjourney 或者 dalle2 生成的人像,如果还能让人像动起来,替我们朗诵或者播报一下 ChatGPT 的内容,不就是一个数字人了么? D-…

2023年 微信生态催生就业收入机会超5000万个

文章目录 微信生态催生就业收入机会超5000万个原因相关情况总结 结语 微信生态催生就业收入机会超5000万个 ​ 近年来,随着移动互联网的普及和发展,微信作为国内最大的社交平台之一,已经成为人们日常生活中必不可少的工具。同时,…

嘉立创EDA专业版网络规则设置

在“设计规则”中的“规则管理”界面将PCB的各个设计规则如线宽规则、差分规则、过孔规则等设置完成之后,就需要将PCB中每个网络所对应的规则进行驱动,那么就可以设置“网络规则”。在“网络规则”中可以对当前PCB内的所有网络进行规则分配,继…

数字图像处理 基于傅里叶变换的图像拼接

一、简述 这里讨论的算法主要是指应用于基于相机拍摄的显微镜的2D图像的拼接。基于2D显微图像的拼接通常只考虑x、y方向的位移。 图像拼接在图像处理中应用广泛。特别是对高分辨率标本成像的需求日益增加。通常,这些标本不适合显微镜的视野。为了克服这一缺点,使用移动样品的…

泡泡玛特发布首支生态公益微纪录片 潮玩IP助力生物多样性保护

5月22日国际生物多样性日,泡泡玛特联合中国环境发布了首支生态公益微纪录片,通过潮玩艺术家AYAN和野生动物保护者初雯雯两个年轻人的故事,用一种全新的方式,唤起年轻人对动物保护和环境保护的关注。 AYAN的DIMOO与蒙新河狸 对于潮…

m1下git的客户端工具及idea配置

背景:换了m2以后,好多软件都不兼容,然后同步代码的事情就一直搁置了,今天记录一下如何用客户端工具同步代码和idea中配置远程仓库 第一步:下载github desktop,有针对m1芯片的,填写上你github的…

Java并发体系-锁与同步-[2]

可见性设计的硬件 从硬件的级别来考虑一下可见性的问题 **1、第一个可见性的场景:**每个处理器都有自己的寄存器(register),所以多个处理器各自运行一个线程的时候,可能导致某个变量给放到寄存器里去,接着…

【Java多线程案例】使用阻塞队列实现生产者消费者模型

前言 本篇文章讲解多线程案例之阻塞队列。主要讲解阻塞队列的特性、实际开发中常用的到的生产者消费者模型,以及生产者消费者模型解耦合、削峰填谷的好处。并且使用 Java 多线程模拟实现一个生产者消费者模型、阻塞队列版的生产者消费者模型。 文章从什么是阻塞队列…

12道c语言的课后习题!

1、计算n的阶乘&#xff08;1*2*3*4*5 n是个数&#xff0c;比如说乘到100&#xff1f;&#xff09; // 计算n的阶乘#include"stdio.h"int main() {int n 0;scanf("%d", &n);int i 0;int ret 1;for (i 1; i < n; i) {ret * i;}printf("…

技术分享| 融合会议协议大解密

anyRTC的融合会议解决方案中&#xff0c;支持H.323协议、SIP协议、GB28181国标协议、私有协议等等&#xff0c;实际在跟客户沟通时&#xff0c;我们常会被问到到底是SIP还是H.323好&#xff1f;客户前期已经建设了视频会议系统&#xff0c;有H.323的、有腾讯会议/Zoom这种互联网…

ESLint驼峰命名法规则校验关闭

目录 1、简单介绍ESLint1.1 ESLint是什么1.2 ESLint的好处1.3 ESLint的规范 2、 驼峰校验提示3、解决方案 1、简单介绍ESLint 1.1 ESLint是什么 ESLint 是一个代码检查工具&#xff0c;用来检查你的代码是否符合指定的规范 例如: 的前后必须有一个空格例如: 函数名后面必须…

郑州市元宇宙产业发展实施方案发布,中创助力元宇宙创新建设!

如果说2022年的“元宇宙”赛道&#xff0c;尚是以Meta为首&#xff0c;而今&#xff0c;政府也纷纷参与其中。 数据显示截至目前&#xff0c;国内已有至少30个地区&#xff08;涉及10个省级行政区&#xff09;颁布了元宇宙相关的支持性政策或征求意见稿&#xff0c;政策文件名中…

神策「数据闭环解决方案」重磅上线,3 大方向助推业务起飞

在通过数据驱动业务增长的落地实践中&#xff0c;企业大多面临以下困扰&#xff1a; 数据源多且杂乱&#xff0c;该如何从 0 开始积累数据助力业务决策&#xff1f; 如何借助数据的力量&#xff0c;洞察业务关键增长点&#xff1f; 如何提升运营效率&#xff1f;怎样提高用户的…

STM32——关于GPIO讲解及标准库应用(基础篇)

简介&#xff1a; STM32是一系列基于ARM Cortex-M内核的32位微控制器。该系列微控制器广泛应用于计算机、通讯、工业自动化、消费电子、汽车电子、医疗仪器及家庭电器等领域。该系列控制器具有高性能、低功耗、智能化等特点。其中&#xff0c;GPIO就是STM32控制器中的一…

Stack 栈的实现与应用

目录 1. 概念 2. 常用的栈的方法 2.1 方法 2.2 代码 3. 自己实现栈 3.1 构造MyStack 3.2 push() 3.3 ensureCapacity&#xff08;&#xff09; 3.4 pop() 3.5 peek() 3.6 empty() 3.7 szie() 4. 栈的应用 1. 概念 栈&#xff08;Stack&#xff09;是一种数据结构&…

02_stack栈

1. Stack 栈是什么&#xff1f; 栈也叫栈内存&#xff0c;主管Java程序的运行&#xff0c;是在线程创建时创建&#xff0c;它的生命期是跟随线程的生命期&#xff0c;线程结束栈内存也就释放&#xff0c;对于栈来说不存在垃圾回收问题&#xff0c;只要线程一结束该栈就Over&…

LabVIEWCompactRIO 开发指南37 在第三方模拟器中执行

LabVIEWCompactRIO 开发指南37 在第三方模拟器中执行 如果需要验证时序和功能&#xff0c;在将LabVIEW FPGA VI编译到硬件之前&#xff0c;可以与三款第三方仿真器进行交互&#xff1a;MentorGraphicsModelSim&#xff08;LabVIEW2013及更早版本&#xff09;、MentorGraphics…