Ruby 面向对象编程之 SOLID

简介: Ruby 面向对象编程之 SOLID

在网上用中文搜Ruby SOLID,出来的很多文章都是英文,不过这也许是我用谷歌搜的结果。

SOLID 原则,是面向对象编程的几个重要概念的英文首字母缩写,也是面向对象编程中最基础的几个概念。最早是由《代码清洁之道》的作者 Bob Martin 和《修改代码的艺术》的作者 Michael Feathers 提出来。

但是,基础,并不意味着很多人都掌握,其实,不掌握的还是很多。对于掌握了的人来说,能用好的也不多。为什么会这样呢?也许因为对我们人类来说,它却恰好是反模式

SOLID 原则是什么

SOLID 不是一个原则,他是一组面向对象设计原则的简称,他代表下面 5 种设计原则:

  • S ingle Responsibility Principle 单一职责原则
  • O pen/Closed Principle 开闭原则
  • L iskov Substitution Principle 里氏替换原则
  • I nterface Segregation Principle 接口隔离原则
  • D ependency Inversion Principle 依赖倒置原则

以上就是 SOLID 中的 5 种面向对象设计原则,下面分别看看他们具体指的是什么。

单一职责原则,即 Single Responsibility Principle

单一职责原则是修改一个类的理由从来不应超过一个。

这条原则虽然看上去简单,但是实施起来非常难。因为你很难去界定一个类的职责。

这个职责的确定有一些技巧,比如说你在实现这个类的时候,不需要去关注其他的类;还有就是你修改这个类的时候,如果影响到这个类的其他职责的实现,那说明我们需要拆分这个类了。

比如说 Rails 中 Model 的职责,主要是用来处理 Ruby 对象和数据库中的相应的表的关系。现在我们的写 REST API 的时候,都需要为用户生成一个 Token。通常的写法会是:

class User < ActiveRecord::Base
...
   def generate_token
     # generate unique token
   end
...
end

但是很显然,生成 Token 这个职责和 User 本身的关系并不大,因此我们应该把它单独提出来。

关闭原则(Open/Closed Principle)

类或者方法,应该对扩展是开放的,对修改是关闭的

有没有一种感觉,突然发现 Rails 项目不用维护了:》

这个原则的主要作用是希望进一步提升我们的代码的模块化。开关原则主要是通过各种设计模式来实现,例如策略模式。

以我们生活中的手机充电器为例:

class Charger
   def charge
       case phone_type
       when android_phone
          use_type_c_for_charging
       when iphone
          use_lightning_for_charging
       end
   end
   private
   def use_type_c_for_charging
       # charging
   end
    def use_ligntning_for_charging
       # charging
    end
end

假如说我们希望支持旧手机,那我们必须修改这个类:

class Charger
   def charge
       case phone_type
       when android_phone
          use_type_c_for_charging
       when iphone
          use_lightning_for_charging
       when old_android_phone
          use_micro_usb_for_charging
       end
   end
   private
   def use_type_c_for_charging
       # charging
   end
    def use_ligntning_for_charging
       # charging
    end
    def use_micro_usb_for_charging
       # charging
    end
end

很显然这违反了开关原则。为了使得我们的代码遵守开关原则,我们可以使用以下代码:

class Charger
   def initialize(phone)
      @phone = phone
   end
   def charge
       @phone.charge
   end
end
class Android
   def charge
       p "Android phone is charging..."
   end
end
class Iphone
   def charge
      p "Iphone is charging..."
   end
end
vivo = Android.new
charger = Charger.new(vivo)
charger.charger

即便后续手机接口又变了,我们可以再声明一个类来实现 charge 的功能,而我们的 Charger 类并不需要改变。

里氏替换原则 L iskov Substitution Principle

里氏替换原则是指任意父类,都可以用它的子类来替换,而且不会出现异常或者错误的结果。

关于里氏替换原则比较经典的例子是矩形和正方形的例子。

class Rectangle
    def initialize(width, length)
       @width = width
        @length = length 
   end
   def caculate_area
       @width * @length
   end
end
class Square < Rectangle
    def initialize(length)
        @width = length
         @length = length
    end
end

假如说我们有段代码:

rec = Rectangle.new(5, 4)
rec.caculate_area  #=> 20

但是如果我们用它的子类,Square 来替换 Rectangle 的时候,我们程序就会报错。

这意味着我们违反了里氏替换原则。所以说这样的抽象是不正确的抽象。

正确的抽象可以让 Rectangle 和 Square 都继承于某个类,比如说类 Shape. 为了让我们的类 Shape 看上去更有用,我故意增加了一个方法,inspect。

class Shape
   def inspect
      puts "I am a #{self.class}."
   end
end
class Rectangle < Shape
   attr_accessor :width, :length
   def caculate_area
       @width * @length
   end
end
class Square < Shape
  attr_accessor :width
   def caculate_area
      @width ** 2
    end
end

可能很多同学会问,这个原则的意义在哪?这个原则的意义在于,遵守里氏替换原则,有助于我们遵守开关原则。

接口隔离原则 I nterface Segregation Principle

接口隔离原则说的是客户端不应该被迫依赖于它不使用的方法。简单来说就是更小和更具体的瘦接口比庞大臃肿的胖接口好。

Ruby 是种动态语言,接口隔离原则主适用于于 Java 等静态语言而言。Ruby 方法的参数不需要指定参数的类型。在 Ruby 中比较出名的是鸭子类型:“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”

所以在动态语言中,你幸福了,可以不用了解这个原则。如果你对这个原则依然感兴趣的话,可以记住三个字: “瘦接口”。

依赖倒置原则D ependency Inversion Principle

依赖倒置原则说的是高层模块 (比如说业务逻辑) 不应该依赖底层模块(比如说数据库查询和 IO),两者都应该依赖其抽象。

依赖倒置原则可以通过鸭子类型和依赖倒置原则共同实现,其实在讲开关原则的时候举的例子,其实也适合本例。

class Charger
   def initialize(phone)
      @phone = phone
   end
   def charge
       @phone.charge
   end
end
class Android
   def charge
       p "Android phone is charging..."
   end
end
class Iphone
   def charge
      p "Iphone is charging..."
   end
end
vivo = Android.new
charger = Charger.new(vivo)
charger.charger

这里高阶的实例charger并不依赖于低阶的对象AndroidIphone。所以它也同时满足了依赖倒置原则。

最后,他们之间的关系如图:屏幕截图 2023-09-06 113648.png

相关文章
|
4月前
|
开发者 测试技术 Android开发
Xamarin 开发者的五大常见问题及解决方案:从环境搭建到性能优化,全面解析高效跨平台应用开发的技巧与代码实例
【8月更文挑战第31天】Xamarin 开发者常遇问题及解决方案覆盖环境搭建至应用发布全流程,助新手克服技术难关。首先需正确安装配置 Visual Studio 及 Xamarin 支持,设置 iOS/Android 测试环境。利用 Xamarin.Forms 和 XAML 实现高效跨平台开发,共享 UI 和业务逻辑代码。针对性能优化,采取减少 UI 更新、缓存计算结果等措施,复杂问题则借助 Xamarin Profiler 分析。
49 0
|
设计模式 Java API
Ruby面向对象编程之SOLID(完全篇)
Ruby面向对象编程之SOLID(完全篇)
Ruby面向对象编程之SOLID(完全篇)
|
设计模式 API 数据库
Ruby面向对象编程之SOLID(一)
Ruby面向对象编程之SOLID(一)
|
Ruby 数据安全/隐私保护 索引
《面向对象设计实践指南:Ruby语言描述》—第1章 1.4节 面向对象编程简介
面向对象的应用程序由对象和它们之间传递的消息构成。其中,消息相对更为重要。但在本节的简介里(以及在本书的前面几个章节里),这两个概念都同等重要。
1754 0
|
7月前
|
Ruby
|
7月前
|
Ruby
|
7月前
|
JSON 数据格式 Ruby
|
7月前
|
调度 Ruby
|
7月前
|
存储 JSON 数据格式
|
7月前
|
Ruby