Tbpgr Blog

Ruby プログラマ tbpgr(てぃーびー) のブログ

RubyでChainOfResponsibilityパターン

概要

GoFデザインパターンChainOfResponsibilityパターンについて。
処理の責務をたらい回しにする。

利点としては
・要求側は処理を呼び出す最初の相手だけ知っていれば良い
・処理の連鎖の修正が容易
・各自の処理に専念できる

欠点としては
・遅いこと

登場人物

Handler = 抽象取扱者
ConcreteHandler = 具象取扱者
Client = 利用者

UML


実装サンプル

サンプル概要

Javaのクラスを自動生成したいとします。
利用者がクラス名を渡すと、コードジェネレーターはクラスの名前を元に
処理対象を取り扱う取扱者を判断して責務をたらい回しにします。
取扱担当者が見つかれば該当者がコードの生成を行い結果を返却します。

ここでは条件として以下の優先順で取扱者を判断します。
・Testで始まるクラスはテスト
・Abstractで始まるクラスは抽象クラス
・それ以外は通常のクラス
とすること。

登場人物

取扱者の管理 = ClassGenerateHandlerManager:取扱者の取扱順を管理するクラス
Handler = ClassGenerateHandler:抽象取扱者
ConcreteHandler = TestClassGenerateHandler:具象取扱者
ConcreteHandler = AbstractGenerateClassHandler:具象取扱者
ConcreteHandler = NormalClassGenerateHandler:具象取扱者
Client = main:利用者

UML


サンプルコード

ClassGenerateHandlerManager

# encoding: Shift_JIS
require_relative './class_generate_handler'
require_relative './normal_class_generate_handler'
require_relative './test_class_generate_handler'
require_relative './abstract_class_generate_handler'

=begin rdoc
= ClassGenerateHandlerManagerクラス
=end
class ClassGenerateHandlerManager
  attr_accessor :class_generater_handler

  def initialize()
    test_class_generate_handler = TestClassGenerateHandler.new
    abstract_class_generate_handler = AbstractClassGenerateHandler.new
    test_class_generate_handler.next=abstract_class_generate_handler
    
    normal_class_generate_handler = NormalClassGenerateHandler.new
    abstract_class_generate_handler.next = normal_class_generate_handler
    
    @class_generater_handler = test_class_generate_handler
  end

  def generate(class_name)
    return @class_generater_handler.generate class_name
  end
end

ClassGenerateHandler

# encoding: Shift_JIS

=begin rdoc
= ClassGenerateHandlerクラス
=end
class ClassGenerateHandler
  attr_accessor :next
  NOT_OVERRRIDE = 'not override error'

  def generate(class_name)
    if is_target class_name
      return done class_name
    else
      return @next.generate class_name unless @next.nil?
    end
  end

  def is_target(class_name)
    raise WebPage::NOT_OVERRRIDE
  end
  
  def done(class_name)
    raise WebPage::NOT_OVERRRIDE
  end
end

TestClassGenerateHandler

# encoding: Shift_JIS
require_relative './class_generate_handler'

=begin rdoc
= TestClassGenerateHandlerクラス
=end
class TestClassGenerateHandler < ClassGenerateHandler
  def is_target(class_name)
    return true unless class_name.scan(/^Test*/).size == 0
    return false
  end
  
  def done(class_name)
    code =<<"EOS"
public class #{class_name}() extends TestCase {

  setUp() {
  
  }
  
  

  tearDown() {
  
  }
}
EOS

    return code
  end
end

AbstractGenerateClassHandler

# encoding: Shift_JIS
require_relative './class_generate_handler'

=begin rdoc
= AbstractClassGenerateHandlerクラス
=end
class AbstractClassGenerateHandler < ClassGenerateHandler
  def is_target(class_name)
    return true unless class_name.scan(/^Abstract*/).size == 0
    return false
  end
  
  def done(class_name)
    code =<<"EOS"
public abstract class #{class_name}() {

}
EOS

    return code
  end
end

NormalClassGenerateHandler

# encoding: Shift_JIS
require_relative './class_generate_handler'

=begin rdoc
= NormalClassGenerateHandlerクラス
=end
class NormalClassGenerateHandler < ClassGenerateHandler
  def is_target(class_name)
    return true
  end
  
  def done(class_name)
    code =<<"EOS"
public class #{class_name}() {

}
EOS

    return code
  end
end

main

# encoding: Shift_JIS
require_relative './class_generate_handler_manager'

generate_handler_manager = ClassGenerateHandlerManager.new
class_name = "TestClass"
puts "#{class_name}を指定"
puts generate_handler_manager.generate class_name

class_name = "AbstractTestClass"
puts "#{class_name}を指定"
puts generate_handler_manager.generate class_name

class_name = "NotAbstractTestClass"
puts "#{class_name}を指定"
puts generate_handler_manager.generate class_name
出力
■TestClassを指定
public class TestClass() extends TestCase {

  setUp() {
  
  }
  
  

  tearDown() {
  
  }
}
■AbstractTestClassを指定
public abstract class AbstractTestClass() {

}
■NotAbstractTestClassを指定
public class NotAbstractTestClass() {

}