継承によって非生成コードから生成コードを別に分ける。
※内容要確認
※書籍追記分
例:データスキーマからクラスを生成する(Javaと少しのRuby)
例題としてデータスキーマからクラスを生成する場合を考える。データスキーマは以下の通り。
firstName : text lastName : text empID : int
サンプルデータ。
martin, fowler, 222 neal, ford, 718 rebecca, parsons, 20
スキーマに対するセマンティックモデル。
class Schema... attr_reader :name def initialize name @name = name @fields = [] end class Field... attr_accessor :name, :type def initialize name, type @name = name @type = type end
スキーマファイルの解析コード。
class Schema... def load input input.readlines.each {|line| load_line line } end def load_line line return if blank?(line) tokens = line.split ':' tokens.map! {|t| t.strip} @fields << Field.new(tokens[0], tokens[1]) end def blank? line return line =~ /^\s*$/ end
生成の乖離によって生成したいコード。
public class PersonDataGen extends AbstractData { private String firstName; public String getFirstName () { return firstName ; } public void setFirstName (String arg ) { firstName = arg; } protected void checkFirstName(Notification note) {}; private String lastName; public String getLastName () { return lastName ; } public void setLastName (String arg ) { lastName = arg; } protected void checkLastName(Notification note) {}; private int empID; public int getEmpID () { return empID ; } public void setEmpID (int arg ) { empID = arg; } protected void checkEmpID(Notification note) {};
生成コードのためのテンプレート。
public class <%=name%>DataGen extends AbstractData { <% @fields.each do |f| %> private <%= f.java_type %> <%= f.name %>; public <%=f.java_type%> <%=f.getter_name%> () { return <%=f.name%> ; } public void <%= f.setter_name %> (<%= f.java_type %> arg ) { <%= f.name %> = arg; } protected void <%= f.checker_name %>(Notification note) {}; <% end %>
class Field... def java_type case @type when "text" : "String" when "int" : "int" else raise "Unknown field type" end end def method_suffix @name[0..0].capitalize + @name[1..-1] end def getter_name "get#{method_suffix}" end def setter_name "set#{method_suffix}" end def checker_name "check#{method_suffix}" end
生成されたクラスを継承したクラス。
public class PersonData extends PersonDataGen { public String getLastName() { return capitalize(super.getLastName()); } public String getFirstName() { return capitalize(super.getFirstName()); } private String capitalize(String s) { StringBuilder result = new StringBuilder(s); result.replace(0,1, result.substring(0,1).toUpperCase()); return result.toString(); } public String getFullName() { return getFirstName() + " " + getLastName(); }
スーパークラス。
class AbstractData... public Notification validate() { Notification note = new Notification(); checkAllFields(note); checkClass(note); return note; } protected abstract void checkAllFields(Notification note); protected void checkClass(Notification note) {}
生成されたクラス。
class PersonDataGen... protected void checkAllFields(Notification note) { checkFirstName (note); checkLastName (note); checkEmpID (note); }
チェックメソッドは空のメソッドをフックしているため、具象クラスでチェックメソッドをオーバーライドする。
class PersonData... protected void checkEmpID(Notification note) { if (getEmpID() < 1) note.error("Employee ID must be postitive"); }
翻訳&要約が微妙ですいません。