Help us understand the problem. What is going on with this article?

Structクラスで単純なクラスを手軽に定義する

More than 1 year has passed since last update.

はじめに

この記事は書籍「プロを目指す人のためのRuby入門」に掲載できなかったトピックを著者自らが紹介するアドベントカレンダーの19日目です。
本文に出てくる章番号や項番号は書籍の中で使われている番号です。

今回はStructクラスで単純なクラスを定義する方法を説明します。

必要な前提知識

「プロを目指す人のためのRuby入門」の第7章まで読み終わっていること。

Structクラスで単純なクラスを手軽に定義する

RubyにはStructという構造体(のようなクラス)を作るクラスが用意されています。このクラスは次のように使います。

# Structを使ってUserクラス(Userの構造体)を作成する
User = Struct.new(:first_name, :last_name)

# Structクラスから作成されたUserクラスを使う
user = User.new('Alice', 'Ruby')

# 属性を参照する
user.first_name
#=> "Alice"
user.last_name
#=> "Ruby"

# 属性を変更する
user.last_name = 'Python'
user.last_name
#=> "Python"

少し不思議なコードですが、Struct.newを呼ぶとClassオブジェクトが返されます。

Struct.new(:first_name, :last_name).class #=> Class

これを定数(上のコードでいうとUser)に代入すると、新しいクラスをその場で定義したのと同じことになるのです。Struct.newの引数に渡したシンボルは、作成したクラスの属性になり、値を読み書きするメソッドが自動的に作成されます。また、作成したクラスのnewメソッドに渡した引数は、それぞれStruct.newで渡したシンボルに対応して作成したクラスの属性にセットされます。

また、Struct.newで指定しなかった属性は読み書きできません。

User = Struct.new(:first_name, :last_name)
user = User.new('Alice', 'Ruby')

# ageという属性は存在しないので読み書きできない
user.age = 20
#=> NoMethodError: undefined method `age=' for #<struct User first_name="Alice", last_name="Ruby">
user.age
#=> NoMethodError: undefined method `age' for #<struct User first_name="Alice", last_name="Ruby">

さらに、Struct.newにブロックを渡すと作成したクラスにインスタンスメソッドを定義することもできます。

User = Struct.new(:first_name, :last_name) do
  # インスタンスメソッドの定義
  def full_name
    "#{first_name} #{last_name}"
  end
end
user = User.new('Alice', 'Ruby')
# インスタンスメソッドの呼び出し
user.full_name
#=> "Alice Ruby"

上のコードは次のようにclass構文を使って書いてもほとんど同じ結果になります。

# Struct.newで返却されたクラスを継承してUserクラスを定義する
class User < Struct.new(:first_name, :last_name)
  def full_name
    "#{first_name} #{last_name}"
  end
end
user = User.new('Alice', 'Ruby')
user.full_name
#=> "Alice Ruby"

このようにStructを使うと、データを保持するための単純なクラスを手軽に作ることができます。とはいえ、普通のクラスとStructで作ったクラスをどのように使い分けるのかは判断が難しいところです。一般的には「わざわざ明示的にクラスを定義するほどでもないケースに向いている」などと言われたりしますが、「じゃあ、それってどんなケース?」となってしまいます。筆者の意見としては初心者のうちはStructを無理に使おうとせず、普通のクラス定義を使う方が変な癖が付かなくて良いと思います。

次回予告

次回は特異クラスについて説明します。

jnchito
SIer、社内SEを経て、ソニックガーデンに合流したプログラマ。 「プロを目指す人のためのRuby入門」の著者。 http://gihyo.jp/book/2017/978-4-7741-9397-7 および「Everyday Rails - RSpecによるRailsテスト入門」の翻訳者。 https://leanpub.com/everydayrailsrspec-jp
https://blog.jnito.com/
sonicgarden
「お客様に無駄遣いをさせない受託開発」と「習慣を変えるソフトウェアのサービス」に取り組んでいるソフトウェア企業
http://www.sonicgarden.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした