类别

  • 在类别定义里使用一致的结构。

    class Person
      # 首先是 extend 与 include
      extend SomeModule
      include AnotherModule
    
      # 接着是常量
      SOME_CONSTANT = 20
    
      # 接下来是属性宏
      attr_reader :name
    
      # 跟着是其它的宏(如果有的话)
      validates :name
    
      # 公开的类别方法接在下一行
      def self.some_method
      end
    
      # 初始化方法在类方法和实例方法之间
      def initialize
      end
    
      # 跟着是公开的实例方法
      def some_method
      end
    
      # 受保护及私有的方法,一起放在接近结尾的地方
      protected
    
      def some_protected_method
      end
    
      private
    
      def some_private_method
      end
    end
  • 如果某个类需要多行代码,则不要嵌套在其它类中。应将其独立写在文件中,存放以包含它的类的的名字命名的文件夹中。

    # 差
    
    # foo.rb
    class Foo
      class Bar
        # 30个方法
      end
    
      class Car
        # 20个方法
      end
    
      # 30个方法
    end
    
    # 好
    
    # foo.rb
    class Foo
      # 30个方法
    end
    
    # foo/bar.rb
    class Foo
      class Bar
        # 30个方法
      end
    end
    
    # foo/car.rb
    class Foo
      class Car
        # 20个方法
      end
    end
  • 倾向使用模块,而不是只有类别方法的类。类别应该只在产生实例是合理的时候使用。

    # 差
    class SomeClass
      def self.some_method
        # 省略函数体
      end
    
      def self.some_other_method
      end
    end
    
    # 好
    module SomeClass
      module_function
    
      def some_method
        # 省略函数体
      end
    
      def some_other_method
      end
    end
  • 当你想将模块的实例方法变成类别方法时,偏爱使用 module_function 胜过 extend self

    # 差
    module Utilities
      extend self
    
      def parse_something(string)
        # 做一些事
      end
    
      def other_utility_method(number, string)
        # 做另一些事
      end
    end
    
    # 好
    module Utilities
      module_function
    
      def parse_something(string)
        # 做一些事
      end
    
      def other_utility_method(number, string)
        # 做另一些事
      end
    end
  • 当设计类型层级时,确认它们符合 Liskov 替换原则

  • 尽可能让你的类型越 SOLID 越好。
  • 永远替类型提供一个适当的 to_s 方法给来表示领域模型。

    class Person
      attr_reader :first_name, :last_name
    
      def initialize(first_name, last_name)
        @first_name = first_name
        @last_name = last_name
      end
    
      def to_s
        "#{@first_name #@last_name}"
      end
    end
  • 使用 attr 系列函数来定义琐碎的访问器或 mutators。

    # 差
    class Person
      def initialize(first_name, last_name)
        @first_name = first_name
        @last_name = last_name
      end
    
      def first_name
        @first_name
      end
    
      def last_name
        @last_name
      end
    end
    
    # 好
    class Person
      attr_reader :first_name, :last_name
    
      def initialize(first_name, last_name)
        @first_name = first_name
        @last_name = last_name
      end
    end
  • 不要使用 attr。使用 attr_reader 和 attr_accessor

    # 差 - ruby 1.9 中就不推荐了
    attr :something, true
    attr :one, :two, :three # behaves as attr_reader
    
    # 好
    attr_accessor :something
    attr_reader :one, :two, :three
  • 考虑使用 Struct.new,它替你定义了那些琐碎的访问器(accessors),构造器(constructor)以及比较操作符(comparison operators)。

    # 好
    class Person
      attr_reader :first_name, :last_name
    
      def initialize(first_name, last_name)
        @first_name = first_name
        @last_name = last_name
      end
    end
    
    # 更好
    Person = Struct.new(:first_name, :last_name) do
    end
  • 不要扩展 Struct.new。它已经是个类了。对它扩展不但引入了无意义的类的层次也会在该文件多次被require时出现奇怪的错误。

  • 考虑加入工厂方法以提供附加的有意义的方式来生成一个特定的类实例。

    class Person
      def self.create(options_hash)
        # body omitted
      end
    end
  • 倾向使用鸭子类型 而不是继承。

    ## 差
    class Animal
      # 抽象方法
      def speak
      end
    end
    
    # 继承超类
    class Duck < Animal
      def speak
        puts 'Quack! Quack'
      end
    end
    
    # 继承超类
    class Dog < Animal
      def speak
        puts 'Bau! Bau!'
      end
    end
    
    ## 好
    class Duck
      def speak
        puts 'Quack! Quack'
      end
    end
    
    class Dog
      def speak
        puts 'Bau! Bau!'
      end
    end
  • 由于类变量在继承中产生的“讨厌的”行为,避免使用类变量(@@)。

    class Parent
      @@class_var = 'parent'
    
      def self.print_class_var
        puts @@class_var
      end
    end
    
    class Child < Parent
      @@class_var = 'child'
    end
    
    Parent.print_class_var # => will print "child"

    如同你所看到的,在类型层级中的所有类其实都共享单独一个类变量。通常情况下应该倾向使用实例变量而不是类变量。

  • 依据方法的目的用途指定适当的可见层级(privateprotected)。别把所有方法都设为 public(方法的缺省值)。我们现在是在写“Ruby”,不是“Python”。

  • 将 publicprotectedprivate 和被应用的方法定义保持一致的缩排。在上下各留一行来强调这个可见性应用于之后的所有方法。

    class SomeClass
      def public_method
        # ...
      end
    
      private
    
      def private_method
        # ...
      end
    
      def another_private_method
        # ...
      end
    end
  • 使用 def self.method 来定义方法。在代码重构时如果修改类名也无需重复多次修改了。

    class TestClass
      # 差
      def TestClass.some_method
        # 省略方法体
      end
    
      # 好
      def self.some_other_method
        # 省略方法体
      end
    
      # 当你需要定义很多个类时,另一种便捷的方式
      class << self
        def first_method
          # 省略方法体
        end
    
        def second_method_etc
          # 省略方法体
        end
      end
    end
以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号

意见反馈
返回顶部