2

Java でインターフェイスを実装して、1 つのアプリケーションでさまざまな種類のデータベースを使用しようとしています。私が考えたのは、共通のインターフェイスと 2 つの静的変数を使用して抽象クラスを作成し、それらをサブクラスによって上書きすることでした。次に、使用可能なすべてのサブクラスのクラスを含む Class[] List を抽象クラスに追加し、使用する正しいクラスを決定できるいくつかの関数を追加したいと考えました。

目標は、まず利用可能なすべてのデータベース タイプのリストを取得し、ユーザーに 1 つを選択させることです。その後、別の関数が名前 (ローカライズ可能) をIDENTIFIERサブクラスで指定された に変換する必要があります。最後に、3 番目の関数は、そのような を与えることにより、オブジェクトのインスタンス化を可能にしますIDENTIFIER

私の抽象クラスは次のようになります。

public abstract class DataBase {
    public static final IDENTIFIER = "";
    public static final NAME = "";
    private static final Class[] dbTypes = new Class[]{PostgreSQL.class, MySQL.class};

    public static String[] getNameList() {
        String[] names = new String[dbTypes.length];
        for(int i = 0; i < dbTypes.length; i++){
            names[i] = dbTypes[i].NAME;       //Cannot access the static variable this way.
        }
        return names;
    }
    public static String getIdentifierForName(String name) {
        for(int i = 0; i < dbTypes.length; i++){
            if(name.equals(dbTypes[i].NAME){       
                return dbTypes[i].IDENTIFIER;
            }
        }
        return "";
    }
    public static DataBase getInstanceOf(String identifier) {
        for(int i = 0; i < dbTypes.length; i++){
            if(identifier.equals(dbTypes[i].IDENTIFIER) {       
                return dbTypes[i].newInstance();
            }
        }
        return null;
    }
}

Child クラスは次のようになります。

public class MySQL extends DataBase {
    public static final IDENTIFIER = "ab.cde.MySQL";
    public static final NAME = "MySQL";
    ...
}
public class PostgreSQL extends DataBase{
    public static final IDENTIFIER = "ab.cde.PostgreSQL";
    public static final NAME = "PostgreSQL";
    ...
}

私の問題は、Class オブジェクトから静的変数にアクセスできないことです。明らかに、dbTypes リストには型指定されたクラスは含まれていません。Array の型を に変更しようとしましたが、クラスをチェックしてからクラスをキャストしようとしたときClass<? extends DataBase>にエラーが発生しましたが、それでも静的変数にアクセスできませんでした。Cannot create a generic array of Class<? extends DataBase>isAssignableFrom()

今のところ、機能している2つのソリューションがあります。

  1. 既存のすべてのサブクラスを各関数if(PostgreSQL.NAME.equals(name)){...}などにハードコードします。ただし、新しいサブクラスを追加する場合は、実装の 1 時点で追加するだけで済みます。

  2. Class[] 配列を使用する代わりに、各クラスのインスタンスで DataBase[] の配列を使用できます。ただし、最終的に必要なのは 1 つだけですが、使用可能な各 DataBase サブクラスをインスタンス化するのは悪い習慣だと思います。

私はそのようなことをしたことがないので、問題に完全に間違ったアプローチをしている可能性もあります。たぶん、このようなことが通常行われる正しい方法を見逃していますか?

ご協力ありがとうございました。

4

4 に答える 4

2

Java には「抽象プロパティ」はありません。DataBase次のように、クラスに2 つの astract メソッドを作成する必要があります。

public abstract class DataBase {

    // No "abstract propeties"

    public abstract String getDBName();
    public abstract String getDBIdentifier();

    // etc etc...

}

そして、各サブクラスで:

public class MySQL extends DataBase {

    public static final IDENTIFIER = "ab.cde.MySQL";
    public static final NAME = "MySQL";

    @Override
    public String getDBName() {
       return NAME;
    }

    @Override
    public String getDBIdentifier() {
       return IDENTIFIER;
    }

    // etc etc...

}

DataBaseクラスを使用する場合は、 (notMySQLまたは)にキャストしてPostgreSQL、2 つの抽象メソッドを呼び出すだけです。

したがって、「データベース クラスを選択する」問題を解決するために、データベースの名前と対応するクラスを含む構成ファイルを作成し、必要に応じてリフレクション ( newInstance()) でインスタンス化します。

別の方法として、リフレクションを使用して、提案されているニキータの回答のような静的変数にアクセスするか、次のように、サポートするデータベースの識別子としてクラスの名前を使用することができます(テストされていません):

public abstract class DataBase {

    private static final Class[] dbTypes = new Class[]{PostgreSQL.class, MySQL.class};

    public static Class getDBClass(String type) {
       for (Class c : dbTypes) {
           if (c.getSimpleName().toLowerCase().equals(type.toLowerCase())) {
               return c;
           }
       }
       return null;
    }

    public static Set<String> getSupportedDB() { // <-- you populate a dropdown menu with this
       Set<String> supported = new HashSet<String>();
       for (Class c : dbTypes) {
           supported.add(c.getSimpleName());
       }
       return supported;
    }

    // etc etc...

}

しかし、私はこのソリューションが好きではなく、使用しません。

于 2013-02-19T09:30:01.087 に答える
0

実際のアプリケーションで利用するために必要以上に、シンプルに保つことをお勧めします。コードをリファクタリングしてさらに複雑にするよりも、拡張する方が簡単です。あなたが言及するもののほとんどは、あなたのアプリケーション自体の実際の要件ではなく、単にあなたの問題解決の成果物です。そして、たまたま、現代のオブジェクト指向言語には必要なものがすべて揃っており、静的プロパティや文字列識別子に頼ることなく、反映せずに優れた設計を実装できます。

事前に知っていることは、ランタイムではなくコンパイラに依存することを忘れないでください。アプリケーションの実行ごとに変更されないことがわかっているものは、ランタイム変数を含まないため、リフレクションは必要ありません。インターフェイス、それらを実装するクラス、さらに重要なことに、これらのクラスを使用して抽象化するファクトリパターンを探します。

interface Database
{
    void query(String sqlString);
}

class MySQLDatabase implements Database
{
    public void query(String sqlString)
    {

    }
}

class PostgreSQLDatabase implements Database
{
    public void query(String sqlString)
    {

    }
}

class DatabaseFactory
{
    Database connectDatabase()
    {
        /// F.e. return new MySQLDatabase();
    }
}

DBA「データベース抽象化レイヤー」全体は、とにかくすでに死に絶え、ODBC問題を解決する他のソフトウェアスタックを生み出しています。これを解決する特定の方法が証明できる利点をもたらすことが確実でない限り、これらに触発されるべきです。もちろん、これを専門的な方法でやりたいのであれば。自分自身を教育したい場合は、必ず、反射、より具体的なオブジェクトの代わりに文字列、および積極的なモジュール性の代わりに密結合を使用してください。

于 2013-02-19T17:36:02.120 に答える
0

テストされていませんが、私はこのようなものを提案します。DataBase.registerDataBase(new DataBase(...)));を呼び出すことにより、データベースを登録できます。これはメインファイルから呼び出すことができます。

    public class DataBase {
          private final static List<DataBase> INSTANCES = new ArrayList<DataBase>();
          private final String identifier;
          private final String name;
          private final Class<?> dbType;
          public DataBase(String identifier, String name, Class<?> dbType) {
           this.identifier=identifier.toString();
           this.name=name.toString();
           this.dbType=dbType;         
          }

          String getIdentifier() {return identifier;}
          String getName()       {return identifier;}
          Class<?> getDbType()   {return dbtype;}

          public synchronized static void registerDatabase(DataBase database) {
            database.getClass();
            INSTANCES.add(database);
            //may check if already registered and either fail or replace it
          }

          public synchronized static List<DataBase> getNameList() {
            return new ArrayList<DataBase>(INSTANCES);
          }

          public synchronized static List<String> getNameList() {
            List<String> names = new ArrayList<String>(INSTANCES.size());
            for (Database db:INSTANCES) names.add(db.getName());
            return names;
          }

          public synchronized static String getIdentifierForName(String name) {       
                for(DataBase db:INSTANCES){
                    if(name.equals(db.getName())) return db;
                }           
            return null;
          }

          public synchronized static DataBase getInstanceOf(String identifier) {
            for(DataBase db:INSTANCES){
                    if(identifier.equals(db.getIdentifier())) return db;
                }           
            return null;
          }
        }
}
于 2013-02-19T10:13:12.800 に答える
0

リフレクションを使用して、各クラスの値を取得できます。

public static String[] getNameList(){
    String[] names = new String[dbTypes.length];
    for(int i=0; i<dbTypes.length; i++){
        Field f = dbTypes[i].getField("NAME");
        names[i] = f.get(null);
    }
    return names;
}

しかし、それは遅いかもしれません。

DBRegistryまた、名前、識別子、およびクラスを含む別の列挙型を作成することをお勧めします。

public enum DBRegistry {
   MYSQL("ab.cde.MySQL", "MySQL", MySQL.class),
   POSTGRESQL("ab.cde.PostgreSQL", "PostgreSQL", PostgreSQL.class);

   private String name;
   private String identifier;
   private Class<?> dbClass;

   private DBRegistry(String identifier, String name, Class<?> dbClass) {
       this.identifier = identifier;
       this.name = name;
       this.dbClass = dbClass;
   }
   // Getters...
}

を使用して、レジストリ内のすべてのアイテムを反復処理できますDBRegistry.values

于 2013-02-19T09:35:30.420 に答える