15

Android プロジェクトにあるいくつかのクラスを見てきましたが、ロジックとデータを混ぜ合わせていることに気付きました。これが私のプロジェクトの可読性とテスト能力にとってどれほど悪いかを認識したので、すべてのサービス ロジックを抽象化してサービス モジュールを分離するためにリファクタリングを行うことにしました。しかし、私は Java のポリモーフィズムに頼ってきたので、道に迷っており、いくつかのガイダンスが必要です。

スーパー データ クラスと 2 つのサブクラスの「変更予定」のレイアウトがあるとします。

public class DataItem {
    /* some variables */ 

    public saveToDB(/* Some Arguments */) {
        /* do some stuff */
    }

    public render() {
        /* render the class */
    }
}

public class ChildDataItemA extends DataItem {
    @Override
    public saveToDB(/* Some Arguments */) {
        super.saveToDB(); 
        /* more specific logic to ChildDataItemA */
    }

    @Override
    public render() {
        /* render logic for ChildDataItemA */
    }
}

public class ChildDataItemB extends DataItem {
    @Override
    public saveToDB(/* Some Arguments */) {
        super.saveToDB(); 
        /* more specific logic to ChildDataItemB */
    }

    @Override
    public render() {
        /* render logic for ChildDataItemB */
    }
}

saveToDB()ここで、メソッドとrender()メソッドをサービス クラスに移動することを考えました。DataItemただし、実行時の型を知らなくても、これらのメソッドをコンパイル済みの型のインスタンスに呼び出すことができる必要がある場合があります。たとえば、次の呼び出しを行いたい場合があります。

List<DataItem> dataList; 
for (DataItem item: dataList) {
    item.saveToDB();
    item.render();
}

さらに、次のことを行うことを考えました。

public class ChildDataItemB extends DataItem {
    @Override
    public saveToDB(/* Some Arguments */) {
        super.saveToDB(); 
        /* more specific logic to ChildDataItemB */
         Service.saveToDBB();
    }

    @Override
    public render() {
        /* render logic for ChildDataItemB */
        Service.renderB();
    }
}

適切なサービスメソッドを呼び出す各サブクラスに「ダミー」メソッドを引き続き保持します。ただし、データクラスはまだサービスについて知っているので、これが本当に私が望む分離を達成するとは思いません (悪い!)。

これを解決する方法についてのアイデアはありますか?

編集:render()saveToDB()は、これらのメソッドの一般的な例にすぎないことに注意してください。そのため、問題は実際には ORM または SQL 関連の手法を選択することではありません。

4

5 に答える 5

5

救助への訪問者パターン。ビジター インターフェイスを作成し、各サービスにこのインターフェイスを実装させます。

public interface DataItemVisitor {
  // one method for each subtype you want to handle
  void process(ChildDataItemA item);
  void process(ChildDataItemB item);
}

public class PersistenceService implements DataItemVisitor { ... }
public class RenderService implements DataItemVisitor { ... }

次に、それぞれにメソッドをDataItem実装させます。accept

public abstract class DataItem {
  public abstract void accept(DataItemVisitor visitor);
}

public class ChildDataItemA extends DataItem {
  @Override
  public void accept(DataItemVisitor visitor) {
    visitor.process(this);
  }
}

public class ChildDataItemB extends DataItem {
  @Override
  public void accept(DataItemVisitor visitor) {
    visitor.process(this);
  }
}

すべてのaccept実装は同じように見えますがthis、各サブクラスで正しい型を参照していることに注意してください。DataItemクラスを変更することなく、新しいサービスを追加できるようになりました。

于 2012-08-04T04:44:09.767 に答える
2

だからあなたがしたい:

List<DataItem> dataList; 
for (DataItem item: dataList) {
    service.saveToDB(item);
    service.render(item);
}

このためには、サービスのシステムをセットアップして、DataItem サブクラスから詳細を知る必要があります。

ORM とシリアライザーは通常、メタデータ システムを介してこれを解決します。たとえば、保存またはシリアル化するプロパティを含む、サブクラスに一致する名前の xml ファイルを見つけることによって解決します。

ChildDataItemA.xml
<metaData>
   <column name="..." property="..."/>
</metaData>

リフレクションと注釈を介して同じ結果を得ることができます。

あなたの場合、ブリッジ パターンのアプリケーションも機能します。

class DataItem {
    public describeTo(MetaData metaData){
       ...
    }    
}

class Service {
   public void saveToDB(DataItem item) {
      MetaData metaData = new MetaData();
      item.describeTo(metaData);
      ...
   }
}

メタデータは保存またはレンダリングから切り離すことができるため、両方で同じことができます。

于 2012-07-29T09:15:40.237 に答える
0

ロジックをデータから分離したい場合は、次のアプローチを試すことができます

データを操作するメソッドを使用せずに、データ クラス DataItem、ChildDataItemA、ChildDataItemB を作成します。

次のようなデータクラスに対するいくつかの操作用のインターフェースを作成します

public interface OperationGroup1OnDataItem {
        public void saveToDB(DataItem dataItem/*plus other params*/) {

        }
        public void render(DataItem dataItem/*plus other params*/) {

        }
        ......
}

OperationGroup プロバイダーを実装するためのファクトリを作成する

public class OperationFactoryProvider {
    public static OperationGroup1OnDataItem getOperationGroup1For(Class class) {
    ....
    }
}

あなたのコードでそれを使用してください:

List<DataItem> dataList; 
for (DataItem item: dataList) {
    OperationGroup1OnDataItem  provider OperationFactoryProvider.getOperationGroup1For(item.class);
    provider.saveToDB(item);
    provider.render(item);
}

クラス (またはクラス fullName) をキーとして配置し、インターフェイスを実装するオブジェクトを値として配置する単純な静的マップを使用してファクトリを実装することを選択できます。何かのようなもの

 Map<String,OperationGroup1OnDataItem> factoryMap= new HashMap<String,OperationGroup1OnDataItem>();
 factoryMap.put(DataItem.class.getName(),new SomeClassThatImplementsOperationGroup1OnDataItemForDataItem());
 factoryMap.put(ChildDataItemA.class.getName(),new SomeClassThatImplementsOperationGroup1OnDataItemForChildDataItemA());

getOperationGroup1For の実装は次のとおりです。

return factoryMap.get(item.getClass().getName());

これは、ロジックをデータから分離する一例です。ロジックをデータから分離したい場合は、ロジック メソッドをデータ クラスから抽出する必要があります。そうでなければ、分離はありません。したがって、すべてのソリューションは、ロジック メソッドを削除することから始めなければならないと思います。

于 2012-08-04T16:04:16.580 に答える
0

renderメソッドとメソッドの「データ」クラスをクリーンアップしsaveToDBます。

代わりに、DataItem のラッパーの階層を作成します (DataItem 階層を正確に模倣する必要はありません)。これらのラッパーは、これらのメソッドを実装するものになります。

さらに、(可能であれば) Hibernate や JPA などの ORM (Object-Relational Mapping) に移行して、saveToDB メソッドを取り除くことをお勧めします。

于 2012-07-27T06:58:11.200 に答える
0

まず、DataItem クラスは、 POJOのように、getter と setter のみを使用し、ロジックをまったく使用せずにクリーンにする必要があります。さらに、 DataItem はおそらく抽象化する必要があります。

さて、ロジックについては、saveToDB部分にORMフレームワークを使用することを他の人が提案したように、あなたはそれがAndroidプロジェクトの原因ではないと言っていましたが、このような他の方法もあります。

したがって、次のロジックを使用して、インターフェイス IDataItemDAO を作成します。

public interface IDataItemDAO<T extends DataItem > {
    public void saveToDB(T data, /* Some Arguments */);
    ... other methods that you need ...
}

DataItem の抽象 DAO を作成し、すべての DataItem の同様のコードを配置します。

public abstract class ChildDataItemADAO impelemets IDataItemDAO<DataItem> {
    @Override
    public void saveToDB(DataItem data, /* Some Arguments */); {
        ...
    }
}

あなたが持っている DataItem クラスごとに DAO を作成するよりも:

public class ChildDataItemADAO extends DataItemDAO impelemets IDataItemDAO<ChildDataItemA> {
    @Override
    public void saveToDB(ChildDataItemA data, /* Some Arguments */); {
        super(data, ...);
        //other specific saving
    }
}

もう 1 つの部分は、正しいインスタンスに正しい DAO を使用する方法です。このため、特定のインスタンスに正しい DAO をもたらすクラスを作成します。if-else ステートメント (または、クラスとDAOのマップで動的に行う)

public DataItemDAO getDao(DataItem item) {
    if (item instanceof ChildDataItemA) {
        //save the instance ofcourse
        return new ChildDataItemADAO();
    }
}

したがって、次のように使用する必要があります。

List<DataItem> dataList; 
for (DataItem item: dataList) {
    factory.getDao(item).saveToDB(item);
}
于 2012-07-29T09:47:23.123 に答える