業務で主に Ruby On Rails での開発を行っていますが、そこで得た知見や、失敗への対応などについて記します。
今回は「アプリケーションの機能を外部にも使えるようにする場合」の実装についてです。
状況
コンテンツ管理の都合上、弊社では「コンテンツを記載したHTMLを開発者以外が作成するが、その中の動的な部分に ERB 形式でコード埋め込んでいる」場合が有ります。
それによって、そのページの動的な部分の出し分けを行えるようになっている、という仕組みです。
しかし、そのコードからは「アプリケーション内のどのメソッドも呼べる」ようになっていたので、以下のような事が生じます。
- 開発の都合上、メソッド名などが変わった場合に、そのコード埋め込み部分でエラーや
DEPRECATED WARNING
が生じる - そのことを防ぐために、実装時にあらかじめ「組み込まれているコンテンツの中身」もチェックする、という作業が生じる
これらの幾つかの面は「運用方法の改善」での対処が考えられますが、そもそも論として
そのコードからは「アプリケーション内のどのメソッドも呼べる」ようになっていた
こと自体が問題です。これを解決する方法を以下のように検討しました。
Facadeパターンを使って、外部からの利用部分を分離する
ここで出てくるのが GoF の提唱したパターン分類の一つで有る「Facadeパターン」というものです。
これは「外部との接続を直に行う」のを改めて、間に窓口となる Facade(建物の正面)
の役を担うオブジェクトを配置する、というものです。
外部からはこの Facade のみを介してアクセスするので
- 外部からのアクセスを限定出来、
- 変更の影響をこのオブジェクトで吸収出来る
ことになります。
具体的には、たとえば以下のような Facade クラス(またはモジュール)を設けます。
# クラス名は ContentFacade でも
module Content
# インスタンスを生成する必要が無いので module_function に
module_function
# Item.count と直接呼んでいたのに代えて Content.item_size と呼ぶ
def item_size
Item.count.to_s(:delimited)
end
# 何かの都合で呼ばなくなったものは deprecate してしまう
def item_count
item_size
end
deprecate item_count: :item_size
end
このクラスのメソッドをインターフェイスとして固定し、常にテストを走らせるようにすれば、
アプリケーション側での更新による不具合を事前に防げます。
なお、Wikipedeia日本語版での この Facadeパターンの説明 では
異なるサブシステムを単純な操作だけを持ったFacadeクラスで結び、サブシステム間の独立性を高める事を目的とする
とあり、あくまでも「複雑な実装を『簡単な操作で行う』ためのもの」とも読み取れますが、GoFの著書 オブジェクト指向における再利用のためのデザインパターン(改訂版) によれば
クライアントとサブシステムの間の結合を減らす
ことについても言及されていて、この Facade パターンがこのような「開発外の部分へのメソッド提供」の部分にも役立てることが分かります。
まとめ
必要に応じて「アプリケーションの機能を外部にも使えるようにする」ことが必要にもなりますが、その時は「アプリケーションの実装(メソッド)をそのまま使ってもらう」のではなくて、Facadeパターンを使って「利用される箇所」を限定するようにするのが大事です。
そのことによって、アプリケーション自体の責務がその Facade部分に集約出来ます。
皆様のアプリケーション開発の参考になりましたら幸いです。