私が書いているSDKでAPIの決定を継承しています。ここでは、次のようにサーバーからドメインオブジェクト(エントリ)をフェッチする必要があります。
blogEntries = client.content_type('blog').entries
ご覧のとおり、ここのcontent_type
プロパティのセッターはパラメーター化されています。この設計を実装するために、私のClient
クラスには、他のオブジェクトに渡す前に@content_type
インスタンス変数を設定する次のようなメソッドがあります。
def content_type(content_type_uid)
@content_type = content_type_uid
// do something with @content_type
end
これで、クラスの他の場所でcontent_typeをフェッチする必要があるときに、上記のメソッドと競合するため、attr_reader
のようなconfiguration.content_type
メソッドを呼び出すことができなくなりました。これで、get_content_type
という別のゲッターメソッドが必要になります。これは実際には非慣用的なRubyです。
content_type=
のような従来のセッターの代わりに、異なる署名のセッターがあるというこの矛盾する状況から自分自身をどのように解決しますか?どのようなトレードオフが最も理にかなっていますか?
理想的には、ゲッターやセッターを完全に避ける必要があります。 OOの基本的な信条は動作の抽象化であり、オブジェクトはdoものだけでなく、「保存」するもの。
絶対にmustにゲッターとセッターが必要な場合は、実際に標準のRuby規則に従うことをお勧めします) :
def content_type=(content_type_uid)
@content_type = content_type_uid
# do something with @content_type
end
それが不可能な場合は、アリティに基づいてメソッドを「オーバーロード」してみてください。
def content_type(content_type_uid = getter = true)
return @content_type if getter
@content_type = content_type_uid
# do something with @content_type
end
これを行う場合は、使用しているドキュメントシステムで、このメソッドを2つの異なる「オーバーロード」メソッドとしてドキュメント化する方法を調べる必要があります。例えば。 [〜#〜] yard [〜#〜] には @overload
タグ:
# @overload content_type
# Gets the content type
# @return [ContentType] The content type
# @overload content_type(content_type_uid)
# Sets the content type to +content_type_uid+
# @param content_type_uid [ContentType::Uid] The UID of the content type to set
def content_type(content_type_uid = getter = true)
return @content_type if getter
@content_type = content_type_uid
# do something with @content_type
end
または、合成属性/メソッドに対するYARDのサポートを使用できます。
# Sets the content type to +content_type_uid+
# @param content_type_uid [ContentType::Uid] The UID of the content type to set
def content_type(content_type_uid = getter = true)
return @content_type if getter
@content_type = content_type_uid
# do something with @content_type
end
# !attribute [r] content_type
# Gets the content type
# @return [ContentType] The content type
ただし、この「オーバーロード」には重大な欠点があることに注意してください。ドキュメントで表現することはできますが、言語で表現する方法はありません。したがって、(私がよく行うように)リフレクションを使用して見慣れないAPIを探索すると、オプションのパラメーターを持つ1つのメソッドのみが表示され、1つのメソッドが実際には2つの異なるメソッドに変装しているという事実は示されません。
method(:content_type).parameters
#=> [[:opt, :content_type_uid]]
通常、オプションのパラメータは、2つの完全に異なる動作を切り替えるのではなく、オプションの引数を提供するために使用されます。
この質問への答えは、 腐敗防止レイヤーとは何ですか、そしてそれはどのように使用されますか? への答えにあるかもしれません。
受け入れられた回答 からの引用の引用:
上記の例は、 Anticorruption Layer がc2wikiでどのように説明されているかに基づいています。
アプリケーションが、自分のアプリケーション内で必要なモデルにモデルが望ましくない、または適用できないデータベースまたは別のアプリケーションを処理する必要がある場合は、AnticorruptionLayerを使用して、そのモデルと自分のモデルとの間で変換を行います。
ファンキーな振る舞いのクラスがあります。奇妙な動作を「修正」してクライアントを非表示にするラッピングクラスを作成することをお勧めします。
class ClientWrapper
attr_reader :content_type
CONTENT_TYPE_BLOG = 'blog'
def initialize(client)
@client = client
end
def blog_entries
@content_type = CONTENT_TYPE_BLOG
@client.content_type(@content_type).entries
end
end
今、あなたはすることができます:
client = ClientWrapper.new SomeClient.new
blog_entries = client.blog_entries
puts client.content_type # -> 'blog'
別の方法として、腐敗防止レイヤーとともに、コンテンツタイプとデータを一緒にパッケージ化する必要があるかもしれません。
class ClientWrapper
CONTENT_TYPE_BLOG = 'blog'
def initialize(client)
@client = client
end
def blog_entries
ClientData.new CONTENT_TYPE_BLOG, @client.content_type(CONTENT_TYPE_BLOG).entries
end
end
class ClientData
attr_reader :content_type, :data
def initialize(content_type, data)
@content_type = content_type
@data = data
end
end
これを使用すると、次のようになります。
client = ClientWrapper.new SomeClient.new
blog_entries = client.blog_entries
puts blog_entries.content_type # -> 'blog'
puts blog_entries.data
私のRubyは少しさびていますが、サブクラスを使用してクライアントデータに独自のバージョンのeach
メソッドを作成できます。
class ClientArrayData < ClientData
def each(&block)
if data.respond_to? :each
data.each block
else
block.call data
end
end
end
または、ClientData
クラスにメソッドを追加するだけです。次に、他の反復可能なオブジェクトと同様に使用できます。
client.blog_entries.each do |entry|
# Do stuff with `entry`
end