1

まず第一に、この質問は少し長くなりますが、私の問題を完全に説明するために、私のプロジェクトについて多くの情報を提供する必要があると感じていますので、ご容赦ください!

私はチャートを頻繁に使用する会社で働いているので、常に scats からチャートを作成しなければならないという問題を回避するために、新しい Java プロジェクトを作成し、私と同僚がこれらを作成するために使用できる「パッケージ」を作成することにしました。チャート。それらをジェネリックと呼ぶことができます。

したがって、このプロジェクトではビルダー パターンと多くのインターフェイスと抽象クラスを使用します。クライアント (開発者) がこれらのインターフェイス、パターン、およびクラスを使用し、フローに合わせてメソッドをオーバーライドするという考え方です。クライアント (開発者) に残された唯一のことは、これらのメソッドを埋めて、自分で UI を作成し、プログラムを公開することです。

プログラムは順調に進んでおり、非常に誇りに思う機能をたくさん作成しました (私は学生なので)。プロセス全体をうまく計画したと思っていましたが、いくつかの問題に遭遇しました!

まず最初に、クラスを示してデータフローを説明します (できるだけ短くします)。

最初は GUI です (これは、ユーザーが常に自分で作成する必要があるクラスですが、組み込みパッケージを使用して、次のコードを使用してチャートを作成できます)。

ChartBuilder cb = new LineChartBuilder();
                  Director d = new Director();
d.buildTypeOne(cb, "Hello", PeriodSelection.HOUR,"");

これで、ディレクターはチャートを作成する準備が整いました。

このPeroidSelection.Hour場合、標準時間を設定する列挙型であり、チャートのカテゴリ軸を開始時間に設定するため、収集されるデータは、1 時間ごとにデータを取得する必要があることを認識します (この場合は 8.00 から 19.00 まで)。これが Enum である理由は、これらのタイプの期間が最終的なものであり、変更できるのは営業日と営業時間のみであり、非常に簡単に変更できるからです! のプレビューは次のPeriodSelection enumとおりです。

    public enum PeriodSelection{
    HOUR(new String[]{"8:00", "9:00", "10:00", "11:00", "12:00", "13:00", "14:00", "15:00", "16:00", "17:00", "18:00", "19:00"}),
    MONTH(new String[]{"Jan", "Feb", "Marts", "April", "Maj", "Juni", "Juli", "Agu", "Sep", "Oct", "Nov", "Dec"}),
    DAYS(new String[]{"Mandag", "Tirsdag", "Onsdag","Torsdag","Fredag","Lørdag","Søndag"});

    private String[] timeIntervals;
    private PeriodSelection(String[] timeIntervals){
        this.timeIntervals = timeIntervals;
    }
    public String[] getTimeIntervals(){
        return timeIntervals;
    }
}

Director に入ると、director はチャートを作成する準備が整いましたが、最初にデータベースからデータを収集する必要があります。

    public void buildTypeOne(ChartBuilder builder, String title, PeriodSelection selection, String queueName){
    try {
        builder.setObjectList(stat.getData(queueName, start, end));
    } catch (DopeDBException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    builder.selection = selection;
    builder.initiate(title);
    builder.createSeries();
    builder.createSymbol();

    builder.createTable();
}

ご覧のとおり、ビルダーは特定の方法で構築されています。これは、チャートがテーブルとチャートの両方で構成されており、これら 2 つをリンクする必要があるためです。チャートについてはあまり詳しく説明しません。質問。

メソッド extendsの最初の行に示されている stat クラスと、buildTypeOne呼び出された抽象クラスはstatisticPattern次のようになります。

public abstract class StatisticPattern {


protected ArrayList<ObjectInterface> cq = new ArrayList<>();
protected ObjectInterface contact;
protected ProviderInterface p;
/**
 * 
 * 
 * {@link Constructor}
 */
public StatisticPattern(){
    try {
        p = new Provider();
    } catch (DopeDBException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}
/**
 * 
 * @param name
 * @param start
 * @param end
 * @return
 * @throws SQLException
 * @throws DopeDBException
 */
protected ArrayList<ObjectInterface> getData(String name, DateTime start, DateTime end) throws SQLException, DopeDBException{
    return cq;
}
/**
 * 
 * @param contact2
 */
protected void processSingleQueueData(ObjectInterface contact2) {
}
/**
 * 
 * @param queueName
 * @throws SQLException
 */
protected void obtainNewData(String queueName) throws SQLException {

}

/**
 * 
 * @param name
 * @param start
 * @param end
 * @return
 */
protected boolean doIhaveIt(String name, DateTime start, DateTime end) {
    return false;
}
/**
 * 
 * @param start
 * @param end
 * @return
 */
protected boolean checkDatas(DateTime start, DateTime end) {
    return start.toDateMidnight().isEqual(end.toDateMidnight());
}
/**
 * 
 * @param start
 * @param end
 * @return
 */
protected Integer daysBetween(DateTime start, DateTime end) {
    return  end.dayOfYear().get()-start.dayOfYear().get();

}

前述のように、このクラスの目的は、ディレクタがこれらのメソッドを見つけて使用できるように、開発者がクラスを拡張してメソッドをオーバーライドすることです。これにより、メソッドの実装方法と入力方法はプログラムごとに異なります。

このプログラムでは、統計クラスは次のようになります。

public class Statistics extends StatisticPattern {
private DateTime start;
private DateTime end;


/**
 *  This class checks whether the program has already collected the data
 * @Override
 */
public ArrayList<ObjectInterface> getData(String name, DateTime start, DateTime end) throws DopeDBException{
    if (this.start.equals(start) && this.end.equals(end)) {
        if (name.equalsIgnoreCase("All")) {
            return cq;
        }else if (doIhaveIt(name, start, end)) {
            return cq;
        }
    }else {
        try {
            obtainNewData(name);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    return cq;
}
@Override
protected void obtainNewData(String queueName) throws SQLException {
    setDates(start, end);
    this.cq = p.obtainData(start, end, queueName);

}

ご覧のとおり、メソッドの独自の実装を作成しました。getDataこのメソッドは、obtainNewDatap (プロバイダー クラス - データベースへの接続とも呼ばれます) からデータを取得するメソッドを呼び出します。

obtainDataご想像のとおり、プロバイダー クラスは、メソッド内にメソッドが 1 つしかない実装とインターフェイスも備えています。

public interface ProviderInterface {

public ArrayList<ObjectInterface> obtainData(DateTime start, DateTime end, String name) throws SQLException;

}

ここでも、開発者はこのメソッドを実装する必要がありますが、好きなように埋めることができます。ここで最も重要なことは、戻り値の型が ObjectInterface 型の ArrayList であることです。

public interface ObjectInterface {

HashMap<String, Integer> data = new HashMap<String, Integer>();
String type = "";

public String getType();
public void addData(String key, Integer value);
public Integer getData(Object key);

}

基本的に、チャートに入力する必要があるすべてのデータを含むハッシュマップを実装する必要があります。このデータは、オブジェクトの作成時に追加され、プロバイダのリストに追加されます。このリストが返され、Director はそれを chartbuilders のデータ リストに設定し、チャートを埋めます。

この場合、チャートを埋めなければならない最終的なオブジェクトは次のようになります。

public class ContactQueue implements ObjectInterface {

    private HashMap<String, Integer> data = new HashMap<String, Integer>();
    private String type;
    public ContactQueue(String type){
        this.type = type;
    }
    public String getType(){
        return type;
    }
    public void addData(String key, Integer value){
        if (data.containsKey(key)) {
            Integer i = data.get(key);
            data.put(key, value+i);
        }else {
            data.put(key, value);
        }

    }
    public Integer getData(Object key){
        return data.get(key);
    }
    public String toString(){
        return type;
    }
}

では質問へ!

収集するデータが 1 種類しかない場合、これらはすべて完全に機能します。しかし、私が現在取り組んでいるプログラムは、5 つの異なるテーブルからデータを取得する必要があり、そのすべてを各チャートに追加する必要があります。データベース テーブルを選択するように設計するにはどうすればよいですか? その特定のテーブルのリストを返しますか?

私はすでに次のコードで試しました:

        if (name.equalsIgnoreCase("Besvarelse")) {
        return besvarelse25(start, end);
    }else if (name.equalsIgnoreCase("intern")) {
        return intern(start, end);
    }else if (name.equalsIgnoreCase("besvarelseProcent")) {
        return besvarelseProcent(start,end);
    }else if (name.equalsIgnoreCase("Email_data")) {
        return email_data(start, end);
    }else if (name.equalsIgnoreCase("Email_Hånd")) {
        return email_haand(start, end);
    }else if (name.equalsIgnoreCase("Email_Antal")) {
        return email_antal(start, end);
    }
    else if (name.equalsIgnoreCase("Henvendelser")) {
        return henvendelser(start, end);
    }

ただし、控えめに言っても、それはちょっと冗長で醜いようです。

クライアント(開発者)が新しいプログラムを作成するたびに変更しなければならない Enum を再度作成することを考えましたが、これが正しい方法であるかどうかはわかりません。

また、プロジェクト全体についてどう思いますか?私はそれを釘付けにしましたか、それとも失敗しましたか?

読んでくれてありがとう、私はあなたの返事を読むのを楽しみにしています

4

2 に答える 2

1

意見:バッキングデータベースに基づいて最後にフィルタリングしているように見えるため、ソリューションはシンプル/エレガントです。はい、それは冗長で醜いですが、同様の情報の5つのテーブルを持つスキーマもそうです(私がこれを正しく読んだ場合)。効果があれば問題ないと思います。

「正しいタイプ」のテーブルを選択する必要があるという懸念がある場合は、それぞれの一般的な品質を見つけることができる場合があります。

のようなもの->

  SELECT 
    generic.specificField AS "genericKey",
    ....
  FROM
    TableOne generic

次に、設計の一定レベルの一貫性を満たす HashMap (またはオブジェクト、呼び出し) を作成できます。

再びのようなもの - >

  for(Field f : resultSet.values())//Pseudo Code, obviously javax.sql is a bit more complex
    hashMap.put(f.getName(), f.getValue());
...
  return genericProcessing(hashMap, start, end);

キー/メソッド (SQL エイリアスで定義されている) を知っているので、簡単な回避策があります。ただし、これがパブリック API である場合は、より「厳密な」(インターフェイス) の方がおそらく適切です。

データに関連性がない場合、デザインはデータを反映していると言えます。それは何も悪いことではありません。

(セカンドオピニオン、上で提案されたファクトリパターンは、抽象化の別の形式にすぎません。Java エコシステムの多くの人々はそれに賛成し、多くの人はそれを嫌っています。私はここで 2 番目のキャンプにいます)。一般的に言えば、最初に問題を抽象化しようとします。抽象化できない場合は、おそらく十分に単純です。あなたの場合、おそらくメソッド/クエリで抽象化できると思います。できない場合は、型にジャンプする (ファクトリがこれを行う方法です) ことがおそらく最善の答えです。

于 2012-12-17T15:23:52.153 に答える
1

正直なところ、私はOPを完全に読んでおらず、質問に直接行きました. 申し訳ありません...
次のようなコードがあるときはいつでも:

if (name.equalsIgnoreCase("Besvarelse")) {
        return besvarelse25(start, end);
    }else if (name.equalsIgnoreCase("intern")) {
        return intern(start, end);
    }else if (name.equalsIgnoreCase("besvarelseProcent")) {
        return besvarelseProcent(start,end);
    }else if (name.equalsIgnoreCase("Email_data")) {
        return email_data(start, end);
    }else if (name.equalsIgnoreCase("Email_Hånd")) {
        return email_haand(start, end);
    }else if (name.equalsIgnoreCase("Email_Antal")) {
        return email_antal(start, end);
    }  

Factory パターンの使用を検討してください。Factory パターンは、この場合の標準的な方法です (Abstract Factory も参照)。

于 2012-12-17T15:11:53.093 に答える