Tbpgr Blog

Employee Experience Engineer tbpgr(てぃーびー) のブログ

RubyでAbstractFactoryパターン

概要

GoFデザインパターンAbstractFactoryパターンについて。
Factoryで利用するオブジェクト一式をまとめて入れ替えることが可能です。

登場人物

AbstractFactory = 工場の抽象クラス
ConcreteAbstractFactory = 工場の具象クラス
AbstractProduct = 製品の抽象クラス
ConcreteAbstractProduct = 製品の具象クラス

UML


実装サンプル

サンプル概要

ある言語のソースコードを生成するプログラムを想定します。
ソースコードを生成するための工場がLanguageFactory。
Ruby用に実装した工場がRubyFactory。
Java用に実装した工場がJavaFactory。
工場から生成される製品がSourceCode。
Ruby用に実装した製品がRubySourceCode。
Java用に実装した製品がJavaSourceCode。

LanguageFactoryに具体的なFactory名を渡し、任意の言語のFactoryを取得したら
create_languageメソッドでクラス名を設定したソースコード生成オブジェクトを取得します。
取得したオブジェクトに対してcreate_fieldでフィールドを設定、create_methodでメソッドを設定し、
get_sourcecodeメソッドを実行すれば自分が指定したクラス名・フィールドリスト・メソッドリストで
ソースコードを出力します。

登場人物

AbstractFactory = LanguageFactory : 言語工場の抽象クラス
ConcreteAbstractFactory = RubyFactory : Ruby工場の具象クラス
ConcreteAbstractFactory = JavaFactory : Java工場の具象クラス
AbstractProduct = SourceCode : 言語の抽象クラス
ConcreteAbstractProduct = RubySourceCode : Ruby言語の具象クラス
ConcreteAbstractProduct = JavaSourceCode : Java言語の抽象クラス
AbstractProduct = Method : メソッドの抽象クラス
ConcreteAbstractProduct = RubyMethod : Rubyメソッドの具象クラス
ConcreteAbstractProduct = JavaMethod : Javaメソッドの具象クラス
AbstractProduct = Field : フィールドの抽象クラス
ConcreteAbstractProduct = RubyField : Rubyフィールドの具象クラス
ConcreteAbstractProduct = JavaField : Javaフィールドの具象クラス

UML


サンプルコード

LanguageFactory

# encoding: Shift_JIS

=begin rdoc
= LanguageFactoryクラス
=end
class LanguageFactory
  NOT_OVERRRIDE = 'not override error'
  NOT_EXIST_CLASS = 'not exist class'

  def self.get_factory(language)
    factory = Object.const_get(language).new
    return factory
    rescue NameError
      raise LanguageFactory::NOT_EXIST_CLASS
  end

  def create_language(class_name)
    raise LanguageFactory::NOT_OVERRRIDE
  end

  def create_method(access_modifier,return_type,method_name,param_list_map)
    raise LanguageFactory::NOT_OVERRRIDE
  end

  def create_field(access_modifier,type,field_name,default_value)
    raise LanguageFactory::NOT_OVERRRIDE
  end
end

RubyFactory

# encoding: Shift_JIS
require_relative './language_factory'
require_relative './ruby_sourcecode'
require_relative './ruby_method'
require_relative './ruby_field'

=begin rdoc
= RubyFactoryクラス
=end
class RubyFactory < LanguageFactory
  
  def create_language(class_name)
    ruby_source = RubySourcecode.new(class_name)
    return ruby_source
  end

  def create_method(access_modifier,return_type,method_name,param_list_map)
    ruby_method = RubyMethod.new()
    ruby_method.method_name = method_name
    ruby_method.access_modifier = access_modifier
    ruby_method.param_list_map = param_list_map
    return ruby_method
  end

  def create_field(access_modifier,type,field_name,default_value)
    ruby_field = RubyField.new()
    ruby_field.field_name = field_name
    ruby_field.access_modifier = access_modifier
    ruby_field.default_value = default_value
    return ruby_field
  end
end

JavaFactory

# encoding: Shift_JIS
require_relative './language_factory'
require_relative './java_sourcecode'
require_relative './java_method'
require_relative './java_field'

=begin rdoc
= JavaFactoryクラス
=end
class JavaFactory < LanguageFactory
  
  def create_language(class_name)
    java_source = JavaSourcecode.new(class_name)
    return java_source
  end

  def create_method(access_modifier,return_type,method_name,param_list_map)
    java_method = JavaMethod.new()
    java_method.method_name = method_name
    java_method.return_type = return_type
    java_method.access_modifier = access_modifier
    java_method.param_list_map = param_list_map
    return java_method
  end

  def create_field(access_modifier,type,field_name,default_value)
    java_field = JavaField.new()
    java_field.field_name = field_name
    java_field.access_modifier = access_modifier
    java_field.default_value = default_value
    return java_field
  end
end

SourceCode

# encoding: Shift_JIS
require_relative './method_'
require_relative './field'

=begin rdoc
= Sourcecodeクラス
=end
class Sourcecode
  NOT_OVERRRIDE = 'not override error'
  attr_accessor :class_name,:field_list,:method_list

  def initialize(class_name)
    @class_name = class_name
  end

  def get_class_start()
    raise Sourcecode::NOT_OVERRRIDE
  end
  
  def get_class_end()
    raise Sourcecode::NOT_OVERRRIDE
  end

  def get_class_sourcecode()
    raise Sourcecode::NOT_OVERRRIDE
  end

  def get_mothod_sourcecode()
    raise Sourcecode::NOT_OVERRRIDE
  end

  def get_field_sourcecode()
    raise Sourcecode::NOT_OVERRRIDE
  end

  def get_sourcecode()
    raise Sourcecode::NOT_OVERRRIDE
  end
end

RubySourceCode

# encoding: Shift_JIS
require_relative './sourcecode'

=begin rdoc
= RubySourcecodeクラス
=end
class RubySourcecode < Sourcecode
  def get_class_start()
    return ""
  end
  
  def get_class_end()
    return "end"
  end

  def get_class_sourcecode()
    return "class #{@class_name}"
  end

  def get_method_sourcecode()
    methods_ = ""
    @method_list.each {|method_|
      methods_ += "\tdef #{method_.method_name}("
      unless method_.param_list_map.nil?
        method_.param_list_map.each {|method_param|
          methods_ += "#{method_param['param_name']},"
        }
        #右端の1文字を削除すること
        methods_ = methods_.slice(0,methods_.size-1)
      end
      methods_ += ")\n\n\tend\n\n"
    }
    return methods_
  end

  def get_field_sourcecode()
    fields = ""
    @field_list.each {|field|
      fields << "\t" + field.field_name
      if field.default_value.nil?
        fields << "\n"
      else
        fields << " = \'#{field.default_value}\'\n"
      end
    }
    return fields
  end

  def get_sourcecode()
    puts get_class_sourcecode
    puts get_field_sourcecode
    puts ""
    puts get_method_sourcecode
    puts get_class_end
  end
end

JavaSourceCode

# encoding: Shift_JIS
require_relative './sourcecode'

=begin rdoc
= JavaSourcecodeクラス
=end
class JavaSourcecode < Sourcecode
  VOID = "void"

  def get_class_start()
    return " {"
  end
  
  def get_class_end()
    return "}"
  end

  def get_class_sourcecode()
    return "public class #{@class_name}"
  end

  def get_method_sourcecode()
    methods_ = ""
    @method_list.each {|method_|
      if method_.access_modifier.nil? || method_.access_modifier.empty?
        methods_ << "\t"
      else
        methods_ << "\t#{method_.access_modifier} "
      end
      if method_.return_type.nil?
        methods_ << JavaSourcecode::VOID
      else
        methods_ << method_.return_type
      end
      methods_ += " #{method_.method_name}("
      unless method_.param_list_map.nil?
        method_.param_list_map.each {|method_param|
          methods_ += "#{method_param['param_type']} "
          methods_ += "#{method_param['param_name']},"
        }
        #右端の1文字を削除すること
        methods_ = methods_.slice(0,methods_.size-1)
      end
      methods_ += ") {\n\n\t}\n\n"
    }
    return methods_
  end

  def get_field_sourcecode()
    fields = ""
    @field_list.each {|field|
      fields << "\t"
      unless field.access_modifier.nil? || field.access_modifier.empty?
        fields << field.access_modifier + " "
      end
      fields << field.field_name
      unless field.default_value.nil?
        fields << " = #{field.default_value}"
      end
      fields << ";\n"
    }
    return fields
  end

  def get_sourcecode()
    print get_class_sourcecode
    puts get_class_start
    puts get_field_sourcecode
    puts ""
    puts get_method_sourcecode
    puts get_class_end
  end
end

Method

# encoding: Shift_JIS

=begin rdoc
= Methodクラス
=end
class Method_
  attr_accessor :method_name,:return_type,:access_modifier,:param_list_map

  def initialize(method_name)
    @method_name = method_name
  end
end


RubyMethod
※今回はクラス内の拡張はなし。継承しただけ

JavaMethod
※今回はクラス内の拡張はなし。継承しただけ

Field

# encoding: Shift_JIS

=begin rdoc
= Fieldクラス
=end
class Field
  attr_accessor :field_name,:access_modifier,:default_value

  def initialize(field_name)
    @field_name = field_name
  end
end

RubyField
※今回はクラス内の拡張はなし。継承しただけ

JavaField
※今回はクラス内の拡張はなし。継承しただけ



main処理

# encoding: Shift_JIS
require_relative './language_factory'
require_relative './sourcecode'
require_relative './ruby_factory'
require_relative './ruby_sourcecode'
require_relative './java_factory'
require_relative './java_sourcecode'

lang_factory = LanguageFactory.get_factory('RubyFactory')
# class名の設定
ruby_source = lang_factory.create_language 'SampleRubyClass'

# フィールドの設定
field1 = RubyField.new('@sample_ruby_field1')
field1.access_modifier = nil
field1.default_value = nil

field2 = RubyField.new('@sample_ruby_field2')
field2.access_modifier = nil
field2.default_value = 'default'

field_list = [field1,field2]
ruby_source.field_list = field_list

# メソッドの設定
method1 = RubyMethod.new('sample_ruby_method1')
method1.return_type = "String"
method1.access_modifier = "private"
method1.param_list_map = nil

method2 = RubyMethod.new('sample_ruby_method2')
method2.return_type = nil
method1.access_modifier = "public"
param_map2_1 = {"param_name" => "param1", "param_type" => "Map"}
param_map2_2 = {"param_name" => "param2", "param_type" => "List"}
param_list = [param_map2_1, param_map2_2]
method2.param_list_map = param_list
method_list = [method1,method2]
ruby_source.method_list = method_list

puts ruby_source.get_sourcecode

puts "−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−"


lang_factory = LanguageFactory.get_factory('JavaFactory')
# class名の設定
java_source = lang_factory.create_language 'SampleJavaClass'

# フィールドの設定
field1 = JavaField.new('sampleJavaField1')
field1.access_modifier = "int"
field1.default_value = nil

field2 = JavaField.new('sampleJavaField2')
field2.access_modifier = "String"
field2.default_value = "\"default\""

field3 = JavaField.new('sampleJavaField3')
field3.access_modifier = nil
field3.default_value = nil

field_list = [field1,field2,field3]
java_source.field_list = field_list

# メソッドの設定
method1 = JavaMethod.new('sample_java_method1')
method1.return_type = "String"
method1.access_modifier = "private"
method1.param_list_map = nil

method2 = JavaMethod.new('sample_java_method2')
method2.return_type = nil
method1.access_modifier = "public"
param_map2_1 = {"param_name" => "param1", "param_type" => "Map"}
param_map2_2 = {"param_name" => "param2", "param_type" => "List"}
param_list = [param_map2_1, param_map2_2]
method2.param_list_map = param_list

method_list = [method1,method2]
java_source.method_list = method_list

puts java_source.get_sourcecode
出力
class SampleRubyClass
	@sample_ruby_field1
	@sample_ruby_field2 = 'default'

	def sample_ruby_method1()

	end

	def sample_ruby_method2(param1,param2)

	end

end

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
public class SampleJavaClass {
	int sampleJavaField1;
	String sampleJavaField2 = "default";
	sampleJavaField3;

	public String sample_java_method1() {

	}

	void sample_java_method2(Map param1,List param2) {

	}

}