概要
GoFのデザインパターンのStrategyパターンについて。
委譲によってアルゴリズムを交換可能にする。
登場人物
Context = コンテキストクラス
Strategy = 抽象戦略
ConcreteStrategy = 具象戦略
実装サンプル
サンプル概要
ある言語のソースコードを実行するプログラムを想定します。
ある言語を利用するLanguageUserが自分が使用する言語実行のインスタンス(LanguageExecutor)をメンバーに持ちます。
言語実行は継承により他言語対応が可能です。例えばRubyExecutorやJavaExecutorなど。
LanguageUserはLanguageExecutorのexecuteメソッドを実行すれば任意のソースコードを任意の言語で実行できます。
例えば、hoge.rbというRubyのソースコードを実行したければメンバーにはRubyExecutorを設定し
RubyExecutor.executeを実行すればrubyコマンドでソースコードを実行します。
次に、hoge.javaというJavaのソースコードを実行したければメンバーにはJavaExecutorを設定し
JavaExecutor.executeを実行すればjavacコマンドでコンパイル後、javaコマンドでクラスファイルを実行します。
登場人物
Context = LanguageUser : 言語の利用者
Strategy = LanguageExecutor : 言語実行
ConcreteStrategy = RubyExecutor : Ruby言語実行
ConcreteStrategy = JavaExecutor : Java言語実行
サンプルコード
LanguageUser
# encoding: Shift_JIS require_relative './language_executor' =begin rdoc = LanguageUserクラス =end class LanguageUser attr_accessor :language_executor,:language,:file_path NOT_OVERRRIDE = 'not override error' def initialize(language) @language = language @language_executor = LanguageExecutor.get_executor(language) end def execute(file_path) puts language_executor.execute file_path puts "#{file_path}を#{language}で実行しました" end end
LanguageExecutor
# encoding: Shift_JIS require_relative './ruby_executor' require_relative './java_executor' =begin rdoc = LanguageExecutorクラス =end class LanguageExecutor attr_accessor :language,:file_path EXECUTOR = 'Executor' NOT_OVERRRIDE = 'not override error' NOT_EXIST_CLASS = 'not exist class' def self.get_executor(language) executor = Object.const_get("#{language}#{LanguageExecutor::EXECUTOR}").new return executor rescue NameError raise LanguageFactory::NOT_EXIST_CLASS end def execute(file_path) raise LanguageExecutor::NOT_OVERRRIDE end end
RubyExecutor
# encoding: Shift_JIS require_relative './language_executor' =begin rdoc = RubyExecutorクラス =end class RubyExecutor def execute(file_path) return `ruby -Ku #{file_path}` end end
JavaExecutor
# encoding: Shift_JIS require_relative './language_executor' require_relative './string_util' =begin rdoc = JavaExecutorクラス =end class JavaExecutor def execute(file_path) `javac #{file_path}` folder_path = StringUtil::get_folder_path file_path file_name = StringUtil::get_file_name file_path file_name_without_extension = StringUtil::get_file_name_without_extension file_name `cd #{folder_path}` return `java #{file_name_without_extension}` end end
LanguageEnum
# encoding: Shift_JIS =begin rdoc = LanguageEnum(もどき)クラス 言語の列挙定数を保持 =end class LanguageEnum LANGUAGE = {:java => "Java",:ruby => "Ruby"} end
main
# encoding: Shift_JIS require_relative './language_user' require_relative './language_enum' java_user = LanguageUser.new(LanguageEnum::LANGUAGE[:java]) ruby_user = LanguageUser.new(LanguageEnum::LANGUAGE[:ruby]) java_user.execute "HelloJava.java" ruby_user.execute "hello_ruby.rb"
StringUtil
# encoding: Shift_JIS class StringUtil def self.to_snake_case(str) ret = str.gsub(/([A-Z].)/,'_\1').downcase ret = ret.slice(1,ret.size) return ret end # パスからファイル名を除外した文字列を取得 def self.get_folder_path(file_path) folders = file_path.split('/') folders.pop folder_path = "" folders.each {|folder| folder_path << folder folder_path << "/" } return folder_path end # ファイル名のみを取得 def self.get_file_name(file_path) folders = file_path.split('/') return folders[folders.size-1] end # 拡張子なしファイル名の取得 def self.get_file_name_without_extension(file_name) file_names = file_name.split('.') return file_names[0] end end
出力結果
Hello!Java HelloJava.javaをJavaで実行しました hello ruby! hello_ruby.rbをRubyで実行しました