Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
読みやすいコード
Search
Takumi Shotoku
December 05, 2019
Technology
1
740
読みやすいコード
Omotesando.rb #53
https://omotesandorb.connpass.com/event/157355/
Takumi Shotoku
December 05, 2019
Tweet
Share
More Decks by Takumi Shotoku
See All by Takumi Shotoku
Automatically generating types by running tests
sinsoku
3
11k
滅・サービスクラス🔥 / Destruction Service Class
sinsoku
8
2.5k
テストを書かないためのテスト/ Tests for not writing tests
sinsoku
1
260
ドメインの本質を掴む / Get the essence of the domain
sinsoku
2
300
"型"のあるRailsアプリケーション開発 / Typed Rails application development
sinsoku
10
2.8k
Let's get started with Ruby && Rails Tips
sinsoku
0
450
LTの敷居を下げる / Lower the threshold for LT
sinsoku
1
380
CircleCIの高速化🚀 / CircleCI faster
sinsoku
3
1.4k
Railsアプリと型検査 / Rails app and type checking
sinsoku
5
1.6k
Other Decks in Technology
See All in Technology
職種の壁を溶かして開発サイクルを高速に回す~情報透明性と職種越境から考えるAIフレンドリーな職種間連携~
daitasu
0
140
CDK CLIで使ってたあの機能、CDK Toolkit Libraryではどうやるの?
smt7174
4
100
フルカイテン株式会社 エンジニア向け採用資料
fullkaiten
0
8.7k
Platform開発が先行する Platform Engineeringの違和感
kintotechdev
4
540
Rustから学ぶ 非同期処理の仕組み
skanehira
1
130
Obsidian応用活用術
onikun94
1
460
Aurora DSQLはサーバーレスアーキテクチャの常識を変えるのか
iwatatomoya
1
720
ガチな登山用デバイスからこんにちは
halka
1
230
現場で効くClaude Code ─ 最新動向と企業導入
takaakikakei
1
210
ハードウェアとソフトウェアをつなぐ全てを内製している企業の E2E テストの作り方 / How to create E2E tests for a company that builds everything connecting hardware and software in-house
bitkey
PRO
1
120
機械学習を扱うプラットフォーム開発と運用事例
lycorptech_jp
PRO
0
230
5分でカオスエンジニアリングを分かった気になろう
pandayumi
0
210
Featured
See All Featured
Documentation Writing (for coders)
carmenintech
74
5k
Imperfection Machines: The Place of Print at Facebook
scottboms
268
13k
Done Done
chrislema
185
16k
Reflections from 52 weeks, 52 projects
jeffersonlam
352
21k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
34
3.1k
What's in a price? How to price your products and services
michaelherold
246
12k
The Pragmatic Product Professional
lauravandoore
36
6.9k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
29
1.9k
Product Roadmaps are Hard
iamctodd
PRO
54
11k
Raft: Consensus for Rubyists
vanstee
140
7.1k
Fantastic passwords and where to find them - at NoRuKo
philnash
52
3.4k
Transcript
読みやすいコード 表参道.rb #53 2019/12/05(Thu) 1
自己紹介 名前: 神速 会社: メドピア株式会社 GitHub: @sinsoku (アイコン右上) Twitter: @sinsoku_listy
(アイコン右下) 2
コードを読みやすく書きたい 3
読みやすいコードとは何か 4
考えてみた • 短いコード • 条件分岐の少ないコード • 適切な命名の変数、クラス、メソッド...etc • メソッド呼び出しのネストが浅い 5
メソッド呼び出しのネストが深い例 def index set_user end private def set_user user_name =
get_user_name_from_api @user = User.find_or_initialize(user_name: user_name) end def get_user_name_from_api api = Sugoi::API.new(token: ENV['SUGOI_TOKEN']) user_name = api.get_user_name convert(user_name) end def convert(user_name) user_name.gsub('-', '_') end 6
浅くした例 def index user_name = api.get_user_name converted_user_name = user_name.gsub('-', '_')
@user = User.find_or_initialize(user_name: converted_user_name) end private def api @api ||= Sugoi::API.new(token: ENV['SUGOI_TOKEN']) end 7
話を戻して... 8
読みやすいコードの条件(主観) • 短いコード • 条件分岐の少ないコード • 適切な命名の変数、クラス、メソッド...etc • メソッド呼び出しのネストが浅い 9
RuboCopでだいたい検出できる • Metrics/AbcSize • Metrics/ClassLength: • Metrics/MethodLength 10
これで読みやすいコードになる! 11
しかし、現実は... # rubocop:disable Metrics/AbcSize Metrics/MethodLength def index # rubocop:enable Metrics/AbcSize
Metrics/MethodLength set_user set_blogs if params[:new] @users = User.where(foo: params[:foo]) .foo .bar .buz elsif params[:create] @users = User.where(foo: params[:foo]).piyo else # ർΕͨͷͰུ end end 12
Metrics/AbcSize を下げるテクニック 1. 変数を定義しない 2. Array(Enumerable)のメソッドを駆使する 3. クラスを抽出する 13
1. 変数を定義しない # bad def count_blogs(users) result = {} users.each
do |user| next unless user.active? result[user.id] = uesr.blogs.size end result end 14
1. 変数を定義しない # good def count_blogs(users) {}.tap do |result| users.each
do |user| next unless user.active? result[user.id] = uesr.blogs.size end end end 15
2. Array(Enumerable)のメソッドを駆使する • if/unlessよりselect/reject # good def count_blogs(users) # active_users
= users.select { |user| user.active? } ͱಉ͡ active_users = users.select(&:active?) {}.tap do |result| active_users.each do |user| result[user.id] = uesr.blogs.size end end end 16
2. Array(Enumerable)のメソッドを駆使する • このケースは to_h で良い # good def count_blogs(users)
users.select(&:active).to_h { |user| [user.id, user.blogs.size] } # ruby 2.5 ͩͱ͜Ε # users.select(&:active).map { |user| [user.id, user.blogs.size] }.to_h end 17
2. Array(Enumerable)のメソッドを駆使する • eachで範囲を-1/+1する必要はない (1..10).to_a #=> [1, 2, 3, 4,
5, 6, 7, 8, 9, 10] (1...10).to_a #=> [1, 2, 3, 4, 5, 6, 7, 8, 9] [:a, :b, :c].each.with_index(1).to_a #=> [[:a, 1], [:b, 2], [:c, 3]] 18
2. Array(Enumerable)のメソッドを駆使する • group_by と transform_values users_per_status = User.find_each.group_by(&:status) #=>
{ "pending" => [<User>, ...], "active" => [<User>, ... } users_per_status.transform_values { |users| users.map(&:user_name) } #=> { "pending" => ["sinsoku", ...], "active" => ["yuki3738", ... } 19
3. クラスを抽出する # app/models/user/ranking.rb class User class Ranking def initialize(user)
@user = user end end def rank score = calculate_score # ུ end private def calculate_score # ུ end end 20
モジュールにするのは # app/models/concerns/rankingable.rb module Rankingable def rank # ུ end
private def calculate_score # ུ end end class User include Rankingable end User.new.private_methods.include?(:calculate_score) #=> true 21
ここから追記 発表に間に合ったのはここまでだった... 22
Class vs Module • Classのprivateメソッドのスコープは狭い • リファクタリングしやすい • Moduleだと影響範囲の調査が少し面倒 •
Moduleは少ないメソッドに依存させるべき • Enumerableは each のみに依存してる • Moduleを上手く使うのは難しい 23
まとめ • RuboCopの指摘はリファクタリングの目安 • 安易に rubocop:disable しない • Arrayのメソッド一覧を読むのがおすすめ •
https://docs.ruby-lang.org/ja/latest/class/Array.html • ModuleよりClassの方がおすすめ 24