LoginSignup
5
4

More than 5 years have passed since last update.

変数のスコープとカプセル化の話

Last updated at Posted at 2015-12-15
class User
  attr_accessor :first_name, :last_name
  def initialize(first_name, last_name)
    @first_name = first_name
    @last_name = last_name
  end
end

class NameConverter
  def main
    users << User.new('田中', '次郎')
    users << User.new('鈴木', '花子')
    users << user.new('山田', '太郎'
    results = []
    users.each do |u|
      results << "#{u.first_name} #{u.last_name} さん"
    end
    results
  end
  def sub
    # mainみたいなロジック
  end
  def subsub
    # mainみたいなロジック
  end
end

↑みたいなロジックがあった時に↓みたいにリファクタリングされることがある、ちょっと極端な例だけど

class User
  attr_accessor :first_name, :last_name
  def initialize(first_name, last_name)
    @first_name = first_name
    @last_name = last_name
  end
end

class CommonConstants
  @@name_suffix = 'さん'
end

class NameConverter
  def convert(first_name, last_name)
    return "#{first_name} #{last_name} #{CommonConstants.name_suffix}"
  end

  def main
    users << User.new('田中', '次郎')
    users << User.new('鈴木', '花子')
    users << user.new('山田', '太郎'
    results = []
    users.each do |u|
      results << convert(u.first_name, u.last_name)
    end
    results
  end
  def sub
    # mainみたいなロジック
  end
  def subsub
    # mainみたいなロジック
  end
end

これはやめたほうがいいよねーという話
↓みたいに、他のクラスに必要ない変数とかメソッドは関係あるクラスに閉じ込めてほしい

class User
  attr_accessor :first_name, :last_name
  @@name_suffix = 'さん'

  def initialize(first_name, last_name)
    @first_name = first_name
    @last_name = last_name
  end

  def formal_name
    "#{@first_name} #{@last_name} #{@@name_suffix}"
  end
end

class NameConverter
  def main
    users << User.new('田中', '次郎')
    users << User.new('鈴木', '花子')
    users << user.new('山田', '太郎')
    results = []
    users.each do |u|
      results << u.formal_name
    end
    results
  end
  def sub
    # mainみたいなロジック
  end
  def subsub
    # mainみたいなロジック
  end
end

この場合NameConverterクラスがUserクラスのプロパティの変換ルールを知ってる必要は全くないし、CommonConstantsみたいな全クラスに晒されるクラスにformal_nameメソッドでしか使わない'さん'を書いておく必要は全くない

'さん'をグローバルな定数にしとけば'さん'が変わった時に、
一斉に変わって素敵!って思うかもしれないけど
こんなにふわっとした名前だと
"#{CommonConstans.name_suffix}タクロース"
みたいな使われ方されるかもしれないし、グローバルに一度晒した定数は簡単に変更するのが難しいので、
CommonConstants.name_suffix2 = 'くん'
みたいな謎の定数が出来かねない
常にUserクラスのメソッドを通してname_suffixにアクセスするってことにすれば、クラスの利用者がname_suffixを意識する必要がなくなるので、定数をクラスの外に出す必要がない

NameConverter#convertだってNameConverterクラスにある以上convert('お', 'っ')みたいな使われてるかもしれないし、ただコードが同じってだけの理由で共通の処理に抜き出してもメンテナンス性は向上しない

重要なのは意味的にまとまりがある単位でメソッドと変数を一つのクラスに閉じ込めて抽象化すること

2番目のコードだとNameConverterクラスは常にconvertのことを気にしないといけなくて、例えばミドルネームに対応してくださいってなった時に、Userクラスにプロパティ増やした上でNameConverterクラスも修正しないといけないけど、3番目のコードだと意味的にまとまりがあるクラスで区切ってあるから、Userクラスだけの修正で済む

複雑な事象に対抗する手段は抽象化なので、出来る限りクラスの抽象度を高めて細かいことを考えなくてもいい作りであってほしいですね!

まとめると
メソッドと変数のスコープは可能な限り狭くしてね
という話です!

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4