不使用字符串使数据库表选择尽可能静态

时间:2012-12-17 14:54:35

标签: java database builder-pattern

首先,这个问题有点长,但为了完整地解释我的问题,我觉得我必须给你很多关于我项目的信息,所以请耐心等待我!

我正在为一家使用图表的公司工作,以避免总是必须从scats创建图表的麻烦我决定创建一个新的java项目并创建一个我和我的同事可以使用的“包”创建这些图表。你可以称它们为Generic。

所以这个项目使用了一个构建器模式和很多接口和抽象类。我们的想法是客户端(开发人员)使用这些接口,模式和类,并覆盖方法以适应流程。对于客户端(开发人员)来说唯一剩下的就是填充这些方法并创建UI以及他自己发布程序。

该计划正在形成良好的形式,我已经创造了很多我非常自豪的功能(因为我是一名学生)我认为我已经计划好整个过程并且很好但我遇到了一些问题!

首先让我向您展示课程并解释数据流(尽可能缩短时间):

首先是GUI(这是一个用户将始终创建自己的类,但他可以使用build in package通过使用以下代码创建他的图表):

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

有了这个,导演现在准备建立一个图表。

PeroidSelection.Hour是一个设置标准时间的枚举,在这种情况下,它将图表类别轴设置为我们的开头,因此收集的数据知道它必须每小时获取数据(从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;
    }
}

进入导演,导演现在已准备好构建图表,但首先必须从数据库中收集数据:

    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();
}

正如你所看到的那样,构建器是以某种方式构建的,这是因为图表包含一个表和一个图表,而这两个必须链接在一起,我不会在图表上详细说明因为它没有对我的问题不屑一顾。

现在,buildTypeOne方法的第一行显示的stat类和名为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();

}

如上所述,eailer这个类的目的是我们的开发人员将扩展类并覆盖方法,以便导演可以找到这些方法并使用它们如何选择实现和填充方法因程序而异。

在这个程序中,统计类看起来像这样:

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方法实现,此方法调用obtainNewData方法,然后从p获取数据(我们的提供者类 - 也称为连接到数据库)。

您可能已经猜到的提供程序类也实现和接口obtainData方法中只有一个方法

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);

}

基本上,它必须实现一个hashmap,其中包含应该填充图表的所有数据。创建对象后将添加此数据,然后将其添加到提供程序中的列表中,然后将返回此列表,Director将设置为图表生成器列表,然后填充图表。

在这种情况下,必须填充图表的最终对象如下所示:

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;
    }
}

现在问题了!

当只收集一种类型的数据时,所有这些都能很好地工作。但是我目前正在处理的程序必须从5个不同的表中获取数据,所有这些表都必须添加到他们的每个图表中我的问题是我如何设计它以便它选择数据库表?并返回该specefic表的列表?

我已尝试使用以下代码:

        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,客户(开发人员)每次想要创建一个新程序时都必须更改但我不确定这是正确的方法吗?

我也很想听听你对整个项目的看法?我把它钉了还是失败了?

感谢您的阅读,我期待着您的回复

2 个答案:

答案 0 :(得分: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 Pattern。工厂模式是这种情况的标准方法(也可以在Abstract Factory上阅读)

答案 1 :(得分:1)

意见:因为您似乎根据后备数据库在最后进行过滤,所以解决方案简单/优雅。是的,它是多余的和丑陋的,但是具有5个相似信息表的模式也是如此(如果我正确阅读的话)。如果它有效,我不认为你有问题。

如果你担心的是你需要选择&#34;正确的类型&#34;您可能能够找到每个表的通用质量:

喜欢的东西 - &gt;

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

然后,您可以创建一个HashMap(或对象,您的调用),它将满足您设计的某种程度的一致性。

再次像 - &gt;

  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,更严格的&#34; (界面)可能是一个更好的选择。

如果您的数据不相关,那么我说您的设计会反映数据。这没什么不对。

第二意见,上面提到的工厂模式只是另一种抽象形式。许多Java生态系统中的人都适合它,而且很多人厌恶它 - 我和#39我在这里的第二个营地。一般来说,我会首先尝试抽象你的问题,如果你不能抽象它,它可能很简单。在你的情况下,我认为你可以用方法/查询来抽象它。如果你不能跳到某种类型(工厂是这样做的一种方式)可能是你最好的答案。