0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

良いコード悪いコードで学ぶ設計入門 【6~7章】 まとめと感想

Last updated at Posted at 2023-04-09

こちらの記事の続きです。

6章 条件分岐

ここでは迷宮化したifswitchの分岐処理を解きほぐす技法について記述されていました。

早期returnや別メソッドへの切り出しなど様々方法が記載されていましたが、特に印象深かったものをまとめます。

interfaceとmapでswitchの重複を解消する

switchでクラスごとの処理をしているとき、それぞれを同じinterfaceでクラス化し、Mapにまとめます。そうするとswitchを使用すること無く、Mapにわたす引き数によって分岐を表現することができます。ちなみにこのようにinterfaceを用いて処理を一斉に切り替える設計がストラテジパターンと呼ぶそうです。

だめなコードの例

    def name(fruit)
        case fruit
        when 'apple'
            puts 'りんご'
        when 'banana'
            puts 'バナナ'
        when 'orange'
            puts 'みかん'
        end
    end

    def price(fruit)
        case fruit
        when 'apple'
            puts 100
        when 'banana'
            puts 150
        when 'orange'
            puts 200
        end
    end
            

ここでは数が3つしか無いですが、条件分岐数が増えたときにコードの修正がしにくくなります。

以下にRubyでストラテジパターンをベースに考えた例です。これはあくまで書籍で記載されていたJavaのサンプルコードををRubyで実装するなら、と考えて記述したものです。(Rubyのinterfaceについては[0]を参考にしました。)

#------------------
# インターフェース
#------------------
    module FruitIF
      def name
        raise NotImplementedError.new("#{self.class}##{__method__} が実装されていません")
      end

      def price
        raise NotImplementedError.new("#{self.class}##{__method__} が実装されていません")
      end
    end
#------------------
# フルーツクラス郡
#------------------
    class Apple
      include FruitIF
      
      def name
        puts 'りんご'
      end
        
      def price
        puts 100
      end
    end

    class Banana
      include FruitIF
      
      def name
        puts 'バナナ'
      end
        
      def price
        puts 150
      end
    end

    class Orange
      include FruitIF
      
      def name
        puts 'みかん'
      end
        
      def price
        puts 200
      end
    end
#-----------------------------
# フルーツの情報を表示するクラス
#-----------------------------
    class FruitInfo
      FRUIT_MAP = {
        apple: Apple.new,
        banana: Banana.new,
        orange: Orange.new
      }
    
      def name(fruit)
        FRUIT_MAP[fruit].name
      end

      def price(fruit)
        FRUIT_MAP[fruit].price
      end
    end

FruitInfo.new().name(:apple)
    => りんご

FruetInfo.new().price(:banana)
    => 150
  

これでswitch(rubyではcase)を使わずに、引き数に渡す物によって出てくる情報を変えることができるようになりました。

感想

分岐がifswitchを使わずに表現できるのはかなり意外で結構テンションが上りました。自分が知っているコードの表現の方法は本当に氷山の一角で、たくさん他にも自分の知らないパターンがあるとなるとこれからいろんなことを勉強するのがより楽しみになりました。ここでいろんな良い構造を知ることができて本当に良かったなぁという感想です。

7章 コレクション

この章ではコレクションのネストを解消する技法が紹介されています。
each文での早期breakに加えて「ファーストクラスコレクション」という技法が紹介されていました。
早期breakについては割愛し、ここでは「ファーストクラスコレクション」についてまとめます。

ファーストクラスコレクション

ファーストクラスコレクションとは「コレクションをインスタンス変数に持ち、それを処理するロジックがまとまったクラス」のことです。配列に対する処理を凝集することができるようになります。

また外部にコレクションを渡すときは変更できなくすることで、外部で新たな処理が書かれて凝集度が低下するのを防ぐことができます。

[1][2]を参考に例を作成しました。

class FruitCollection
  attr_reader :fruits

  def initialize(fruits)
    @fruits = fruits
  end

  def business_logic
    .....
  end
end

感想

ファーストクラスコレクションはrubyでも実装できそうですが、いまいちどこで使えばいいのかわからずこれから理解を深めていこう、という印象です。一方で配列の早期breakなどは簡単に理解でき、すぐにでも取り入れていこうと思います。

参考

[0] Rubyでインターフェースクラスを実現する
https://qiita.com/developer-kikikaikai/items/b1e2bd1c2bbc223de534

[1] Rubyで作るファーストクラスコレクション
https://qiita.com/gashiura/items/999a8c36e47a07fa4b27#%E3%83%A1%E3%83%AA%E3%83%83%E3%83%88

[2] Rubyでファーストクラスコレクションをベースに、検索結果をオブジェクト化するhttps://tech.itandi.co.jp/entry/2022/11/08/180000

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?