LoginSignup
10
2

More than 5 years have passed since last update.

Primitive-obsession(基本データ型への執着)

Posted at

オブジェクト指向を用いた設計を考えて調べものをしていた時にPrimitive-obsession(基本データ型への執着)という言葉を知ったので自分なりにまとめておきました。

Primitive-obsessionとは

「Primitive-obsession(基本データ型への執着)」とは、ドメインの概念を表すのに基本データ型を用いることを言います。
たとえば、メッセージを表すのに文字列を使う、金額を表すのに整数を使う、なんらかのオブジェクトを表すのに構造体・ディクショナリ・ハッシュを使うといったものです。

なにが良くないのか?

たとえば電話番号を表すのに文字列や数値を使っていた場合、電話番号特有の処理などを書きたい場合(-を抜いた数値型に変換したり、その逆をしたりする処理)、そのオブジェクト自体のメソッドとして記述できないので処理用の関数がバラバラに置かれることになります。
そうなるとメンテナンス性が下がるし、変更にも弱くなりやすいです。

そういう時にはPhoneNumberというクラスを作って、PhoneNumberのインスタンスを使うという設計にした方が電話番号に関する処理がカプセル化して良い設計になります。

コード例

名前と電話番号を持っているUserクラスを元にPrimitive-obsessionになっている例とそれを改善した例を示しながら説明します。

まず以下のPrimitive-obsessionになっているUserクラスをみて下さい。

user.rb
# Primitive-obsessionになっているUserクラス
class User

  def name
    self.name
  end

  #
  # nameにユーザーの名前に文字列を代入する
  #
  def name=(string)
    self.name = string
  end

  def phone_number
    self.phone_number
  end

  #
  # phone_numberに電話番号を表す数値を代入する
  #
  def phone_number=(integer)
    self.phone_number = integer
  end

end

一見問題ないように見えますが、要件が増えてくると複雑になっていきます。

例えば、ユーザーの名前を苗字(lastname)と名前(firstname)で分けて管理したい、電話番号をハイフン付きで表示したい、などの要件が出てきたとき、それらの処理を行うメソッドをすべてUserクラス内に記述しないといけません。

そうすると、Userクラスが電話番号と名前という複数の責任を負わないといけないため、オブジェクト指向の原則である単一責任の原則から外れてしまい、すぐにクラスが肥大化してしまいます。

では、今度はPrimitive-obsessionを改善した例を以下に示します。

user.rb
#
# Primitive-obsessionを改善したUserクラス
#
# User.nameはUserNameクラスのインスタンスになっている
# User.phone_numberはPhoneNumberクラスのインスタンスになっている
#
class User

  def name
    self.name =|| new UserName()
  end

  def name=(user_name)
    self.name       = user_name
  end

  def phone_number
    self.name =|| new Telephone()
  end

  def phone_number=(phone_number)
    self.telephone = telephone
  end

end

#
# ユーザー名を管理するクラス
#
class UserName

   def initialize(first_name=null, last_name=null)
      self.first_name = first_name
      self.last_name  = last_name 
   end 

   def first_name
      self.first_name    
   end

   def first_name=(string)
      self.first_name = string   
   end

   def last_name
      self.last_name
   end

   def last_name=(string)
      self.last_name = string
   end

end

#
# 電話番号を管理するクラス
#
class PhoneNumber

  def initialize(integer)
     self.number = integer
  end

  def number
     self.number
  end

  def number=(integer)
     self.number = integer
  end

end

前回と違い、電話番号はPhoneNumberクラス、ユーザーの名前はUserNameクラスで表現するように変更しました。

これにより、ユーザーの名前に関する責任はUserNameクラス、電話番号に関する責任はPhoneNumberクラスに分割することが出来ました。

このような設計を行うことで、Userクラスが負っていた複数の責任を分割してクラスを小さく出来るだけでなく、例えば求人サイトなどの企業を表すCompanyクラスなどが出来た場合にはPhoneNumberクラスを使いまわす事ができます。

終わりに

いままではフレームワークの流儀に合わせたクラス設計(MVC)ぐらいしかしてこなかったのであまりオブジェクト指向を強く意識したことがなかったですが、勉強するとなかなか発見が多くて結構面白いです。

自分はいまRubyでプログラムを書くことが多いので、これを気にちゃんとオブジェクト指向の書き方を勉強し直したいと思います。

10
2
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
10
2