概要
GoFのデザインパターンのBridgeパターンについて。
クラス〜サブクラスの機能の階層。
抽象クラス〜具象クラスの実装の階層。
それぞれを独立させることで保守性を高める。
新たな機能、新たな実装の追加が容易となる、
登場人物
Abstraction = 抽象クラス
RefinedAbstraction = 改善した抽象化
Implementor = 実装者
ConcreteImplementor = 具体的な実装者
実装サンプル
サンプル概要
ある言語のソースコードを生成するプログラムを想定します。
CodeGeneratorは機能の階層です。
指定したCodeGenerateImplでコードの標準出力を行うクラス。
CodeFileGeneratorはCodeGeneratorを継承したクラスでコードのファイル出力機能を
追加してあります。
CodeGenerateImplは実装の階層です。
RubyCodeGeneratorはソースコードの出力を行います。
RubyTestCodeGeneratorはテストコードの出力を行います。
登場人物
Abstraction = CodeGenerator : 言語生成の抽象クラス
RefinedAbstraction = CodeFileGenerator : 言語生成のファイル出力稲生が追加された具象クラス
Implementor = CodeGenerateImpl : 言語生成の実装用抽象クラス
ConcreteImplementor = RubyTestCodeGenerator : 言語生成実装抽象クラス
ConcreteImplementor = RubyCodeGenerator : 言語生成実装抽象クラス
その他
GenerateUtil = コード生成用汎用クラス(キャメルケースからスネークケースへの変換)
サンプルコード
CodeGenerator
# encoding: Shift_JIS require_relative './code_generate_impl' require_relative './ruby_code_generator' require_relative './ruby_test_code_generator' =begin rdoc = CodeGeneratorクラス =end class CodeGenerator attr_accessor :ruby_test_codegenerator,:ruby_codegenerator GENERATE = {:code => 1,:test_code => 2,:both => 3} INCOLLECT_GENERATE_TYPE = 'incorrect generate_type' def initialize() @ruby_codegenerator = RubyCodeGenerator.new @ruby_test_codegenerator = RubyTestCodeGenerator.new end def generate_code(class_name, generate_type) ret = "" case generate_type when GENERATE[:code] ret = @ruby_codegenerator.generate_code class_name when GENERATE[:test_code] ret = @ruby_test_codegenerator.generate_code class_name when GENERATE[:both] ret = @ruby_codegenerator.generate_code class_name ret += @ruby_test_codegenerator.generate_code class_name else raise CodeGenerator::INCOLLECT_GENERATE_TYPE end return ret end end
CodeFileGenerator
# encoding: Shift_JIS require_relative './code_generator' =begin rdoc = CodeFileGeneratorクラス =end class CodeFileGenerator < CodeGenerator def generate_code_to_file(class_name, generate_type) # 基底クラスのgenerate_codeから文字列を取得 code = generate_code(class_name, generate_type) file_name = GenerateUtil::to_snake_case(class_name) file_name = "test_" + class_name if generate_type == GENERATE[:test_code] # ファイルを出力 File.open("./" + file_name + ".rb",'w'){|output_file| output_file.write code } end end
CodeGenerateImpl
# encoding: Shift_JIS =begin rdoc = CodeGenerateImplクラス =end class CodeGenerateImpl NOT_OVERRRIDE = 'not override error' def generate_code(class_name) raise CodeGenerateImpl::NOT_OVERRRIDE end end
RubyCodeGenerator
# encoding: Shift_JIS require_relative './code_generate_impl' =begin rdoc = RubyCodeGeneratorクラス =end class RubyCodeGenerator < CodeGenerateImpl def generate_code(class_name) code =<<"EOS" # encoding: Shift_JIS class #{class_name} # 以下に実装コードを追加 end EOS return code end end
RubyTestCodeGenerator
# encoding: Shift_JIS require_relative './code_generate_impl' require_relative './generate_util' =begin rdoc = RubyTestCodeGeneratorクラス =end class RubyTestCodeGenerator < CodeGenerateImpl def generate_code(class_name) # クラス名をスネークケースに変換 snake_case_class_name = GenerateUtil::to_snake_case(class_name) code =<<"EOS" # encoding: Shift_JIS require 'test/unit' require_relative './#{snake_case_class_name}' class Test#{class_name} < Test::Unit::TestCase # 以下にテストコードを追加 end EOS return code end end
main
# encoding: Shift_JIS require_relative './code_generator' require_relative './code_file_generator' code_generator = CodeGenerator.new result = code_generator.generate_code "SampleRubyClass", CodeGenerator::GENERATE[:both] puts result code_generator = CodeFileGenerator.new code_generator.generate_code_to_file "SampleRubyClass", CodeGenerator::GENERATE[:test_code] code_generator.generate_code_to_file "SampleRubyClass", CodeGenerator::GENERATE[:code]
generate_util.rb
# encoding: Shift_JIS class GenerateUtil def self.to_snake_case(str) ret = str.gsub(/([A-Z].)/,'_\1').downcase ret = ret.slice(1,ret.size) return ret end end
標準出力
# encoding: Shift_JIS class SampleRubyClass # 以下に実装コードを追加 end # encoding: Shift_JIS require 'test/unit' require_relative './sample_ruby_class' class TestSampleRubyClass < Test::Unit::TestCase # 以下にテストコードを追加 end
ファイル出力確認
Feb 20 23:09 sample_ruby_class.rb Feb 20 23:09 test_SampleRubyClass.rb
※中身は上記の標準出力内容のまま