|
事業仕分けによる次世代スーパーコンピューターの開発予算削減について、どうお考えですか?
|
Inversion of Controlパターンでコンポーネント間の結びつきを弱めるはじめにInversion of Control(IoC:制御の反転)パターンはDependency Injectionパターンとも呼ばれ、最近のJ2EEコミュニティではよく利用されています。Spring 、PicoContainer、HiveMindのように、IoCパターンを使用して軽量J2EEコンテナを開発しているオープンソースプロジェクトもいくつかあります。 しかし、IoCは新しい概念ではありません。このパターンは数年前から利用されています。IoCパターンでは、インターフェイス、継承、ポリモーフィズムといったオブジェクト指向設計の原則および特徴を使用して、ソフトウェアコンポーネントの結び付きを弱め、コンポーネントの再利用とテストが容易になるようなソフトウェア設計を実現します。 本稿では、IoCパターンの概要を説明し、オープンソースのIoCフレームワークをまったく実装せずにIoCパターンをソフトウェア設計に取り入れる方法を紹介します。 IoCデザインパターンクラスAとクラスBの間に、クラスAがクラスBのサービスを使用するという関係があるとします。この関係を実現する一般的な方法は、クラスAの内部でクラスBをインスタンス化することです。この方法でも動作上は問題ありませんが、クラス間の結び付きが強くなってしまいます。クラスAを修正せずにクラスBを簡単に変更することができないからです。 クラス間の結び付きを弱めるには、クラスBのインスタンス(オブジェクト"b")をクラスAのインスタンス(オブジェクト"a")に注入する つまり、オブジェクト"a"がオブジェクト"b"の参照を取得する方法の制御が反転しています。オブジェクト"a"は、オブジェクト"b"の参照を取得する責任を持ちません。その代わりに、
Configuratorオブジェクトのメリット この リスト1と図1は、クラスAがクラスBを使用する設計の簡単な例です。 図1 オブジェクト"a"がオブジェクト"b"を直接作成する ![]() リスト1 クラスAがクラスBを直接参照する
public class A{ private B b; public A(){ b=new B(); } リスト1は、次のような設計上の条件を前提としています。
上記の条件がどれか1つでも変更された場合は、リスト1のコードを修正しなければなりません。たとえば、クラスBの設計を変更して、デフォルトコンストラクタを使用する代わりにクラスC(図2を参照)を受け取るようにした場合は、リスト1をリスト2のように変更することになります。 図2 オブジェクト"a"がまずオブジェクト"c"を作成し、オブジェクト"c"を渡すことでオブジェクト"b"を作成する ![]() リスト2 クラスAがクラスBとクラスCを直接参照する
public class A{ private B b; public A(){ C c=new C(); b=new B(c); } このリスト2も、いくつかの設計上の条件を前提にしています。今度はオブジェクト"a"がオブジェクト"b"とオブジェクト"c"の両方を所有しています。クラスBまたはクラスCを大幅に変更した場合は、クラスAも修正の必要があります。つまり、暗黙的な前提に基づいた単純なクラスの単純な設計では、将来的な保守に大きな負担がかかるおそれがあります。 ここで紹介したのはごく単純な例ですが、さまざまなクラスを使用する一般的なアプリケーションであれば、変更がどれだけ難しいか容易に想像できるでしょう。 それに対して、IoCパターンを使用する設計の場合は、オブジェクト"b"を作成する責任をオブジェクト"a"からIoCフレームワークに移し、そのフレームワークでオブジェクト"b"を作成してオブジェクト"a"に注入するようにします。これにより、クラスAをクラスBの修正から切り離すことができます。オブジェクト"a"はオブジェクト"b"への参照を必要としますが、この点は、IoCフレームワークがオブジェクト"b"をオブジェクト"a"に注入することで解決されます。 リスト3は、前述のリストのクラスAをIoCパターンを使用するように修正したものです。 図3 IoCフレームワークがオブジェクト"b"を作成し、それをオブジェクト"a"に注入する ![]() リスト3 クラスAはsetBを通じてクラスBへの参照を取得する
public class A{ private B b; public A(){ } public setB(B b){ this.b=b; } } リスト3は、次のような設計上の条件を前提としています。
この設計上の条件を見ると、クラスAとクラスBの結び付きが弱くなっていることがわかります。どちらのクラスも、相手に影響を与えることなく個別に修正できます。もちろん、クラスBのパブリックメソッドに変更があった場合は、クラスAも変更する必要があります。しかし、オブジェクト"b"の作成方法と管理方法は、オブジェクト"a"の実装内では定義されていません。その代わりに、IoCフレームワークがオブジェクト"a"内の IoCフレームワークの種類いくつかのオープンソースIoCフレームワーク(Spring、PicoContainer、HiveMind など)は、IoCパターンをサポートしています。IoCの基本原則は単純ですが、これらのフレームワークはそれぞれ異なる実装をサポートしており、異なるメリットを実現しています。 IoCパターンには3種類の実装方法があり、それぞれ「Setterベース」、「コンストラクタベース」、「インターフェイスベース」と呼ばれています。ここではそれぞれの方法について簡単に説明します。詳細については、各フレームワークのホームページを参照してください。 SetterベースIoC このタイプのIoCでは、 この方式の主な短所は、 コンストラクタベースIoCこのタイプのIoCでは、コンストラクタを使用してオブジェクトの参照を設定します。この方式の主な長所は、参照されるオブジェクトを知っているのが作成者だけであるという点です。いったんオブジェクトが作成されると、そのオブジェクトを使用するクライアントコードは、参照されるオブジェクトを認識しません。 この方式は、すべてのアプリケーションで使用できるわけではありません。たとえば、デフォルトコンストラクタを必要とする外部APIを使用する場合は、「SetterベースIoC」を採用しなければなりません。Springの実装では主に「コンストラクタベースIoC」が使用されています。 インターフェイスベースIoCこのタイプのIoCでは、IoCフレームワークの特殊なインターフェイスをオブジェクトに実装することで、IoCフレームワークがオブジェクトを適切に注入できるようにします。この方式の主な長所は、オブジェクト参照を設定するための外部設定ファイルが必要ないことです。その代わりに、IoCフレームワークのマーカーインターフェイスを実装することで、オブジェクトをどのように結合するかをIoCフレームワークに認識させます。これはEJBを使用するのに似ています。EJBコンテナは、オブジェクトをインスタンス化して自身にフックさせる方法を認識しています。 この方式の主な短所は、マーカーインターフェイスを使用するために、特定のIoCフレームワークとの結び付きが強くなってしまうことです。Apache Avalonはこの方式に基づいていますが、このプロジェクトは既に閉鎖されています。 IoCの例新しいプロジェクトを開始する場合は、必要に応じていずれかのオープンソースIoCフレームワークを選択することができます。しかし、既存のプロジェクトでIoCパターンを使用する場合は、IoCをサポートする独自のクラスを作成する必要があります。オープンソースのIoCフレームワークは既成のコンポーネントや充実した機能を提供してくれますが、これらを使用せずに、IoCパターンをサポートする一連のクラスを独自に作成することもできます。ここではその方法を紹介します。 たとえば、顧客データを処理する DataSource まずインターフェイスを設計し(リスト4を参照)、顧客データの取得および保存に使用する共通メソッドを定義します。 リスト4 顧客データの読み書きに使用する共通インターフェイス
public interface DataSource { public Object retrieveObject(); public void setDataSourceName(String name); public String getDataSourceName(); public void storeObject(Object object); } XMLDataSource リスト5の リスト5 XMLファイルから顧客データを取得するクラス
public class XMLDataSource implements DataSource { private String name; /** * Default Constructor */ public XMLDataSource() { super(); } /** * Retrieve Customer data from XML Source and construct * Customer object */ public Object retrieveObject() { //get XML data, parse it and then construct //Customer object return new Customer("XML",10); } /** * Set the DataSource name */ public void setDataSourceName(String name) { this.name=name; } /** * Return DataSource name */ public String getDataSourceName() { return name; } /** * Store Customer into XML file */ public void storeObject(Object object) { //Retrieve customer data and store it in //XML file } } RelationalDataSource リスト6の リスト6 リレーショナルデータベースから顧客データを取得するクラス
public class RelationalDataSource implements DataSource { private String name; /** * Default constructor */ public RelationalDataSource() { super(); } /** * Using the DataSource retrieve data for Customer and build a * Customer object to return it to the caller */ public Object retrieveObject() { //get data for Customer object from DB and create a //Customer object return new Customer("Relational",10); } /** * Set the DataSource name */ public void setDataSourceName(String name) { this.name=name; } /** * Return the name of the DataSource */ public String getDataSourceName() { return name; } /** * Store Customer into relational DB */ public void storeObject(Object object) { //store the customer data into Relational DB } } Customer リスト7の リスト7 顧客データを格納するクラス
public class Customer { private String name; private int age; /** * Default Constructor */ public Customer(String name, int age) { this.name=name; this.age=age; } /** * @return Returns the age. */ public int getAge() { return age; } /** * @param age The age to set. */ public void setAge(int age) { this.age = age; } /** * @return Returns the name. */ public String getName() { return name; } /** * @param name The name to set. */ public void setName(String name) { this.name = name; } } CustomerService リスト8の リスト8 ServiceConfiguratorを通じてDataSourceの参照を取得するCustomerServiceクラス
public class CustomerService { private DataSource dataSource; private Customer customer; /** * Constructor in which DataSource object is injected. Based on the * ioc.properties this object can either refer to RelationlDataSource or * XMLDataSource */ public CustomerService(DataSource dataSource) { super(); this.dataSource=dataSource; customer=(Customer)dataSource.retrieveObject(); } /** * Modify Customer name * @param name */ public void updateCustomerName(String name) { customer.setName(name); } /** * Modify Customer age * @param age */ public void updateCustomerAge(int age){ customer.setAge(age); } /** * * @return Customer name */ public String getCustomerName(){ return customer.getName(); } /** * * @return Customer age */ public int getCustomerAge(){ return customer.getAge(); } } ServiceConfigurator リスト9の 作成された 図4に 図4 ServiceConfiguratorを使用するIoC ![]() 図5 ServiceConfiguratorのUMLダイアグラム ![]() リスト9 IoCを使用してDataSourceをCustomerServiceに注入するServiceConfigurator
public class ServiceConfigurator { public static final String IoC_CONFIG_FILE="ioc.properties"; private Properties props; private HashMap serviceRegistery; private static ServiceConfigurator thisObject; /** * This method first checks if there is a ServiceConfigurator instance * exist, if not creates a one, stored it and returns it * @return */ public static ServiceConfigurator createServiceConfigurator(){ if(thisObject==null){ thisObject=new ServiceConfigurator(); } return thisObject; } /** * Private Constructor makes this class singleton */ private ServiceConfigurator() { props = new Properties(); serviceRegistery=new HashMap(); loadIoCConfig(); createServices(); } /** * Load the IoC_CONFIG_FILE properties file * */ public void loadIoCConfig(){ InputStream is = this.getClass().getClassLoader().getResourceAsStream(IoC_CONFIG_FILE); try { props.load(is); is.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } /** * Create the CustomerService by getting the DataSource name from the * properties file. The CustomerService object is stored in the * serviceRegistery so that it will be retrieved when requested. * During the construction of CustomerService the DataSource object * is injected into it. So the CustomerService can access the DataSource * to retrieve the Customer. * */ public void createServices(){ String dataSourceName=props.getProperty("dataSource"); DataSource dataSource=null; if(dataSourceName!=null){ try { dataSource=(DataSource)Class.forName(dataSourceName).newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } //name of the DataSource is retrieved from the properties file dataSource.setDataSourceName(props.getProperty("name")); CustomerService customerService=new CustomerService(dataSource); serviceRegistery.put(CustomerService.class,customerService); } /** * Stored Service is retrieved from serviceRegistery for the given * Class object * * @param classObj * @return */ public Object getService(Class classObj){ return serviceRegistery.get(classObj); } } この CustomerServiceTester リスト10の リスト10 CustomerServiceを使用するJUnitテスト
public class CustomerServiceTester extends TestCase{ private ServiceConfigurator serviceConfig; /** *Default Constructor */ public CustomerServiceTester() { super(); } /** * Create ServiceConfigurator */ public void setUp() throws Exception{ super.setUp(); serviceConfig=ServiceConfigurator.createServiceConfigurator(); } /** * Test CustomerService and check for Customer * @throws Exception */ public void testCustomerService() throws Exception{ CustomerService custService=(CustomerService)serviceConfig.getService(CustomerService.class); assertNotNull(custService); custService.updateCustomerAge(30); custService.updateCustomerName("Mani"); assertEquals(30,custService.getCustomerAge()); assertEquals("Mani",custService.getCustomerName()); } Service ConfiguratorとService Locatorの違い リスト9の 実際、リスト8の Service Locatorの内部実装を変更すれば、 両者の主な違いは、 一方、Service Configuratorの場合は、 では、Service Locatorに比べてService Configuratorを使用した場合のメリットは何でしょうか。Service Configuratorを使用した場合は、 さらに、リスト1とリスト2のところで説明したとおり、 何を注入するか 図5に示したとおり、 実際には、 今回の例では、 このアプリケーションで予想されるのは、顧客データの取得/保存方法の変更だけです。現時点では、顧客データはリレーショナルデータベースかXMLファイルに格納されています。将来的には、オブジェクトデータベースに保存したり、Webサービスを通じて取得したりする可能性もありますが、どちらのシナリオでも、顧客データの取得と保存を行う新しいクラスを作成すれば対処できます。したがって、上記の前提に基づき、今回のサンプルでは 終わりに 本稿では、IoCパターンの概要を説明し、このパターンを使用するオープンソースフレームワークを簡単に紹介しました。さらに、 著者紹介Mani Malarvannan(Mani Malarvannan)
Cybelink Systemsのコンサルタント。ここ数年はオブジェクト指向プログラミングとソフトウェアパターンに基づくWebベースアプリケーション開発に従事。
|
【Graphic Design Forum】
流動的媒体と静的媒体に関する見解(11月18日) |
![]() |
「IT の耳」
|
![]() |
百式のネットビジネス研究
|
![]() |
週刊-サイト別アクセス状況データ
|
![]() |
海外ソーシャルウェブに学ぶ成功の秘訣
ゲーム業界を襲う世界的な激震。ソーシャルゲーム急成長のインパクト(11月19日) |
![]() |
今さら聞けない初歩からのアクセス解析
サイトリニューアル前のアクセス解析活用法(11月19日) |
![]() |
成約率、反応率を上げる Web 文章術
文章力を磨き、キャッシュを生み出す Web サイト に(11月19日) |
![]() |
「Webからの脅威」―その傾向と最新対策
|
![]() |
ROI向上のための戦略的WebPR
|
![]() |
スマートにソーシャルウェブを構築しよう
社員力を生かすソーシャルメディアポリシー(11月17日) |
![]() |
DevX
Erlangを使った並列処理プログラムの作成(11月17日) |