初めに
縁があり、通っているプログラミングスクールのメンター(質問対応者)さんから次のことを勧められましたメソッド化に力を入れた方がいい
と。。。
ということで今回はそのメソッド化についての基本知識?考え方?を自分なりにまとめてみました!!
注意事項
※自分用でまとめてますので、分かり辛かったらすいません。また理解不足・誤りがあったらコメントにてご指摘頂けますと幸いです。m(__)m<ヨロシクオネガイシマス
前提条件
①今回はrails
を題材にして話を進めていきます。
②次の二つの知識についてある程度理解済みであること ※私の場合は60%ぐらいでした・・・多分(;^ω^)
・「メソッド」について理解していること
・「オブジェクト指向」について理解していること
※もしここで不安な方がいましたら、以下のプルダウンのところを開いてリンク先を一読してみてください。
③posts_conntroller
にて@post = Post.find(params[:id])
が定義されていること
以上の3点だけです。
開発環境
・rails 6.1.7・ruby 3.1.2
・raty 3.1.1
・jquery 3.6.1
・bootstrap 4.5
メソッド化とは
簡単にまとめると
オブジェクト指向(「モノ(どんな奴でどう動く)」に注目した考え方のこと)を踏まえた処理をメソッド(オブジェクト指向における「操作」を定義したもののこと)化することです
これだけだと「(。´・ω・)ん?」てなるので ※これ自分ですw
例を用いて説明していきます。
ちょっと詳しく解説
例えば次のプログラムがあるとします。
##とある投稿サイトの詳細ページ
<% if @post.user == current_user %>
<%= link_to "編集する", edit_post_path(@post.id), class: "btn btn-sm btn-success" %>
<%= link_to "削除する", post_path(@post.id), data: { confirm: '本当に消しますか?' }, class: "btn btn-sm btn-danger",method: :delete %>
<% end %>
意味合いとしては・・・「投稿主
とログイン中のuser
が同一人物かどうか」を確認する処理になります。
これを次のように書き換えることをメソッド化といいます。 ※めちゃくちゃ簡単にまとめるとこんな感じです |д゚)
##モデルファイルの方
def written_by?(current_user)
user == current_user
end
##Viewページの方
<% if @post.written_by?(current_trader) %>
<%= link_to "編集する", edit_trader_post_path(@post.id), class: "btn btn-sm btn-success" %>
<%= link_to "削除する", trader_post_path(@post.id), data: { confirm: '本当に消しますか?' }, class: "btn btn-sm btn-danger",method: :delete %>
<% end %>
まとめ
ちょっとまとめてみると次のようになります。①まとめたい処理達(オブジェクト)を見つける ※View
以外にもcontroller
でも可能
②モデルファイル
と元のViewページ or Conntroller
を編集(メソッド化)する
これについても初めて聞いたときは「(。´・ω・)ん?」となったので ※あ、これも自分ですw
次の節にて詳しく説明します!! ガンバリマス(/・ω・)/
①まとめたい処理達(オブジェクト)を見つける
メソッド化する目的
これは大きく分けて2つの理由があります。
①DRY原則を守るため
②見えないエラーを防ぐため
ではこれも深掘り&解説していきます!!
DRY原則を守るため・・・について深掘りしよう
1 . まずそもそもDRY原則
って何?となった方 ※毎度お馴染み、これ自分です(/ω\)ハズカシィ
参考資料を添付しますので、よかったら一読してみてください
2 . なぜこの考え方が必要なのか・・・
「同じ処理文を何回も書くことで書き間違い
(ヒューマンエラー)が生じてしまうため」
「チーム開発の際など、違う書き方をして混乱
してしまう可能性があるため」
これ以外にもあるという方!!
※ご縁があり今回話を聞かせていただいた方(現場で実際に働いている方です)の話をもとにまとめてました。
もしこれ以外にも理由があると思う方は、ぜひコメントにて教えて下さいm(__)mオネガイシヤス
これについて、詳しく知りたい方へ
まずはヒューマンエラーについて解説します。
※ヒューマンエラーについては次のリンクを参考にしてください → ヒューマンエラーとは? 【簡単に】意味、対策、具体例、種類
例えばスペルミス
が挙げられます。
先程紹介したものと同じViewファイルのプログラミングです。
##とある投稿サイトの詳細ページ
<% if @post.user == current_user %>
<%= link_to "編集する", edit_post_path(@post.id), class: "btn btn-sm btn-success" %>
<%= link_to "削除する", post_path(@post.id), data: { confirm: '本当に消しますか?' }, class: "btn btn-sm btn-danger",method: :delete %>
<% end %>
これの<% if @post.user == current_user %>
の部分が<% if @post.user == crrent_user %>
だった場合、エラーが発生します。
→ もうお分かりだと思いますがスペルミスが原因です(current_user
のcurrent
の部分でu
が抜けてることが分かると思います)(´゚д゚`)イヤ,ワカリヅラ!!
続いて違う書き方をして混乱してしまう可能性について解説します。
例えばさっきの<% if @post.user == current_user %>
の部分が<% if @post.user.id == current_user.id %>
だった場合、
→ 書き方的にはどちらも間違っていませんし、今回の例は1行だけというのもあってパッと見た感じで理解できると思います。
がしかし、現場では数行あるオブジェを違う書き方にしたせいで
混乱してしまうことがあるらしいです。。。((( ゚Д゚;))))コワ!!
つまり書き方やオブジェクト指向的には間違っていないけど、書き方が違うだけ
で混乱してしまうということでした(-_-;)ムズカシイ
見えないエラーを防ぐため・・・について深掘りしよう
1 . まずそもそも見えないエラー
って何?イメージが付かないとなった方、次のプルダウンのところを一読してみてください。
見えないエラーとは
簡単にまとめると・・・
書き方は間違っていないけど、オブジェクト指向(要はやってほしい挙動)に合っていない
ことを言います。
例)
先程紹介した、お馴染みのViewファイルです。
##とある投稿サイトの詳細ページ
<% if @post.user == current_user %>
<%= link_to "編集する", edit_post_path(@post.id), class: "btn btn-sm btn-success" %>
<%= link_to "削除する", post_path(@post.id), data: { confirm: '本当に消しますか?' }, class: "btn btn-sm btn-danger",method: :delete %>
<% end %>
今回も<% if @post.user == current_user %>
の部分を題材に解説します(/・ω・)/ガンバリマス
早速ですが、<% if @post.user == current_user %>
の部分が<% if @post.user = current_user %>
の場合、どういう挙動になるか想像してみてください。因みに両者の違いは「=」の数です。
お分かりかもしれませんが、答えは前者が同じかどうか
後者は代入
の意味合いになります。
このようにプログラムの書き方が合っている
+ オブジェクト指向(挙動)が違う
ことを見えないエラーとよく言うそうです( ..)φハジメテシッタカモ
2 . さっきの「違う書き方をして混乱してしまう可能性がある」との違いは
→ オブジェクト指向(要するにやってほしい挙動)が合っているかいないか
です。
詳しい解説はこちら
まずオブジェクト指向とは「モノ(どんな奴でどう動く)」に注目した考え方のこと
でした
参考資料はこちら → オブジェクト指向○○とは |「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典
この考え方を今回に置き換えると次のようになります。
・どんなモノがあるのか =取得済みのUser情報(@post.user)とログイン中のUser情報(current_user)の2つがある
・どんな性質の奴らなのか(属性) =取得済みのUser情報 → 表示中の情報、ログイン中のUser情報 → 今回の処理だけで使いたい情報
・どんな動きができるか =取得済みのUser情報 & ログイン中のUser情報 → 主キー以外の情報も取得可能(紐づけすればできるため)
・実際に何をしているのか(したいのか)=ログイン中のUserと取得済みのUser情報が合っているかどうかを判断(主キーを使って)
次に、お馴染みのViewファイルを使っていきます。
##とある投稿サイトの詳細ページ
<% if @post.user == current_user %>
省略
<% end %>
先程の解説と同じように、<% if @post.user == current_user %>
の部分が<% if @post.user = current_user %>
の場合、どういう挙動になるか想像してみてください。因みに両者の違いは「=」の数です。
答えは前者が同じかどうか
後者は代入
の意味合いでした。
ここで少し前のオブジェクト指向のところ(厳密には4つのポイントのところ)に戻って考えてみてほしいんですが、実際に何をしているのか
の部分が違ってくることがお分かりいただけると思います。
それを考慮してまとめる以下の様な考え方になります。
違う書き方をして混乱してしまう → 本来してほしい挙動をする → オブジェクト指向が合っている
見えないエラー → 本来してほしい挙動をしない → オブジェクト指向が合っていない
解説は以上となります( ..)φメモメモ
少々話が脱線気味になってきたので、ここでメソッド化する目的について話を戻したいと思います。
まとめると次のような考え方になります。
①DRY原則を守るため → スペルミスなどのヒューマンエラーを防ぐ
②見えないエラーを防ぐため → 挙動がおかしいエラーを防ぐ
長くて分かり辛い解説を読んでくださった方には申し訳ございませんが、要するにこんな感じです |д゚)スイマセン
②モデルファイルと元のViewページ or Conntrollerを編集(メソッド化)する
ではここからは手作業を入れていきます(ノД`)・゜・。ヤットダヨ
メソッド化の手順 + それに対してのポイントをマスターしよう
メソッドの手順は次の通りなります※今回の例をメソッド化する時の手順ですので、参考程度にお願いいたします。m(__)m手順①メソッド化するものを探す または 決める(今回は省略)
手順②必要な部分をコピー(元データがない場合は、作成)
手順③メソッド化するモデルファイルを決める&メソッドを作成
手順④ ③で作成したメソッドでコピーしたものを貼り付ける(元データがない場合は、作成)
手順⑤実際に処理したいところ(今回はViewファイル)で定義する(呼び出す)
手順に対してのポイントは作業と一緒に紹介します(^^ゞガンバリマス
手順②必要な部分をコピー(元データがない場合は、作成)
もう何回も見てきた例のプログラムを使っていきます。
##とある投稿サイトの詳細ページ
<% if @post.user == current_user %>
省略
<% end %>
これの@post.user == current_user
の部分をコピー・・・手順①終わりです(;^ω^)ナヌ?!
ここでのポイントは?
それはズバリ、必要な箇所
をコピーすることです。
まずは完成版のモデルファイルを見てみましょう
##作成したメソッド
def written_by?(current_user) #ログイン中のUser情報(current_user)をメソッド処理( written_by?() )にかける
user == current_user #本当は self.user == current_user である(selfは紐づけしている時は省略可能)
#そして今回はpostモデルなので、self == post と考えてOK
#つまり post.user == current_user となる※内部的な意味合いで
#今までの情報を考慮して、まとめると @post.user == current_user と同じ挙動ができることになる
end
因みに公式?書き方?については以下の通りとのことでした。※メンター情報です
##メソッドの公式
def メソッド名?(View や conntroller から持ってきた 引数 )
処理内容
end
結果論の話になりますが、コピー元のオブジェクトと完成版のメソッドを見比べた時、同じ書き方をしている箇所があります。
trader == current_user
の部分です。
つまり今回のポイントである「必要な箇所
をコピーする」=「trader == current_user
の部分」となります。
因みに、ここでの参考資料は
1.1 AttributeMethodsモジュール|Rails ガイド
[Rails]selfについての理解を深めたい[初心者]
+メンターさん情報です
手順③メソッド化するモデルファイルを決める&メソッドを作成
メソッド作成先のモデルファイル:Post
モデル
メソッド名:written_by?()
で作成
・・・以上で手順③終わりです( ゚Д゚)ハヤ
ここでのポイントは?
・作成先のモデルの決め方
・主役(レシーバ)
を決めること
・メソッド名はSVO「S(主語)はO(目的語)をV(述語動詞)する」
まずは作成先のモデルの決め方について・・・
答えから説明すると候補として「Post or User」の2択になります
この2択になった理由としては最初のプログラム(以下のViewページ)にて<% if @post.user == current_user %>
の部分がありました。
この行限定で、この部分に関係するモデル
は「Post or User」の2つのみになります。
よって候補は「Post or User」の2択になったわけです
##とある投稿サイトの詳細ページ
<% if @post.user == current_user %>
省略
<% end %>
候補が出たら、選択フェーズになりますが、今回はPostモデルを選びました。
選んだ理由としては
・呼び出しているViewページはPostに属するものだった(そこで呼び出している)ため
・Userだと大元過ぎると考えたため
ということで、ここで「モデルの決め方」についての説明を終わりにします。
次に 主役(レシーバ) を決めることについて・・・
まずそもそもレシーバ
って何?となった方は、「 . の左側」で覚えておきましょう。私もそう覚えました(´▽`)エヘヘ
以下リンクが参考資料です
Ruby レシーバについて|teratail
rubyのレシーバとは|Qiita
話を戻して・・・
今回の主役(レシーバ)は@post
になります。
具体的には最初にお見せした完成版のViewページにて@post.written_by?(current_trader)
の中の@post
になります
参考として完成版のプログラムを載せときます。
##完成版のViewページ
<% if @post.written_by?(current_trader) %>
省略
<% end %>
ここで疑問に思うのが、「なぜ@post
なのか?」
理由は
①先程Post
モデルにメソッドを作ることを決めたため
②もともとあったデータでも@post
の位置がレシーバの位置だから
まず理由①について・・・
先程の手順までで決まったことを振り返ると、
今回作成するメソッド( written_by?()
)はPost
モデル内で作られることになる。
そしてここからが重要になりますが、
メソッドが作成されるモデル(今回はPost
)とレシーバ(今回は@post
)は同じ属性(今回はPost)でないといけません。
これは「そういうルールがあるんだ(´゚д゚`)ヘェ~ 」で覚えた方がいいかもしれません※個人の意見です
まとめると 「メソッド( written_by?()
)の属性」 = 「@post
の属性」 となることが重要、、、みたいです(´゚д゚`)ヘェ~
次に理由②について・・・
これはみれば一目瞭然なので、解説はなしにします。※悪しからずm(__)m
##もともとあった方の記述
<% if @post.user == current_user %>
省略
<% end %>
手順④ ③で作成したメソッドでコピーしたものを貼り付ける(元データがない場合は、作成)
⓪今までのことを考慮したメソッドを用意
##完成前のモデルファイル0
def written_by?()
end
①コピーを張り付ける
##完成前のモデルファイル1
def written_by?()
@post.user == current_user
end
②エラーが発生しないように少し編集
・@post
を宣言していない
・今回はself
が使える条件下である
##完成前のモデルファイル2
def written_by?()
self.user == current_user
end
③self
はレシーバの場合、省略可能のため・・・
##完成前のモデルファイル3
def written_by?()
user == current_user
end
まだ未完成状態ですが、手順④は以上になります。
ここでのポイントは?
・エラーを無くすこと
・やってほしい挙動かどうか
今回のポイントはチェックするだけなので、解説は割愛します。
手順⑤実際に処理したいところ(今回はViewファイル)で定義する(呼び出す)
⓪今までのことを考慮した記述を用意
##完成前のViewページ
<% if @post.written_by?() %>
省略
<% end %>
①今回はログイン中User
がチェック対象になるので・・・
##完成後のViewページ
<% if @post.written_by?(current_user) %>
省略
<% end %>
②引数(current_user
)が決定したので、モデルファイルも・・・
##完成版のモデルファイル
def written_by?(current_user)
user == current_user
end
ここでのポイントは?
・引数を決める
・引数を決めたらモデルファイルに記述すること(解説は割愛します)
引数とは・・・引数 |「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典
引数の決め方について・・・
①どういった挙動をしてほしいかを考慮する
②欲しい情報は何か?
まず①から考えていきましょう。
今回してほしい動作については、さんざん言ってきましたが「ログイン中のUser
が投稿主
と同一人物かどうか」をチェックすることです。
今の考え方を踏まえて②を考えます。
まず投稿主
の情報は既に@post
に入っている(つまり@post.user) → 手元に揃っているのでわざわざ再度宣言しなくてもいいことになる
次にログイン中のUser
の情報は何も定義していない → 手元にないので、どこかから持ってくる必要がある → current_user
を使う
結論・・・
ログイン中のUser
の情報をcurrent_user
を使うことで取得&引数として利用。といった感じになります(ノД`)メチャムズ
最後に
とてつもなく長文でしたが、ここまでお付き合いいただきありがとうございました ^^) _旦~~オツカレサマデス
ありがたいことに、メンター(質問対応者)さんに教えてもらった考え方?概念?を覚えるために今回記事にしてみました。
実はメソッド化やオブジェクト指向・DRY原則など、今回の記事を書いた際に初めて知ったことが多々あります。
何度も申し訳ございませんが、初学者の立場でなので誤字脱字や誤った情報
についてはコメントで教えていただけると幸いです。勉強になるので!!よろしくお願いいたします。m(__)mオネガイシヤス
まとめ
メソッド化の手順は次の通りなります
手順①メソッド化するものを探す または 決める(今回は省略)
手順②必要な部分をコピー(元データがない場合は、作成)
手順③メソッド化するモデルファイルを決める&メソッドを作成
手順④③作成したメソッドでコピーしたものを貼り付ける(元データがない場合は、作成)
手順⑤実際に処理したいところ(今回はViewファイル)で定義する(呼び出す)
手順に対してのポイントは次の通りなります
ポイント①もともとあった処理文(必要な箇所のみ)をコピー
ポイント②主役(レシーバ)を決める
ポイント③作成先のモデルファイルとメソッド名を決める
ポイント④メソッドの中身(さっきコピーした文)を移植する
ポイント⑤実際に処理したいところで定義する(呼び出す)