设计模式以跟踪复杂过程的部分结果

Mat*_*tyf 12 java oop algorithm design-patterns

我面临一个编程问题,我不知道如何以面向对象灵活的方式解决.我想到了一些糟糕的解决方案,但我正在寻找一个好的解决方案.我用Java开发,所以我更喜欢Java思想,但任何面向对象的想法都是受欢迎的.

我一直在寻找可以帮助我的想法,设计模式或一些算法,但我不知道哪个术语或名称给我的问题,所以我找不到任何线索.

问题:

摘要:

我需要跟踪对一组实体进行不同更改的过程的部分结果.我需要这个向用户报告表格报告中每个计算"步骤"的详细信息.而且我还需要将此集合保存在数据库中.

详情:

我维护的软件有一个类似于这个的实体:

public class Salary {
    private Date date;
    private BigDecimal amount;
}
Run Code Online (Sandbox Code Playgroud)

这是一个集合,如下所示:

List<Salary> thisYearSalaries;
Run Code Online (Sandbox Code Playgroud)

这组实体可以通过一组"任务"修改,具体取决于一些规则:

  • 申请一定的税(从一组不同的税收)
  • 计算金额的未来值(更多信息)
  • 折扣金额以保持低于最大值
  • 等等...

例如:

public void processSalaries(List<Salary> theSalaries) {
    applyTax(theSalaries, Taxes.TAX_TYPE_1);
    (...)
    getFutureValue(theSalaries, someFutureDate);
    (...)
    restrictToAMaximum(theSalaries, Maximum.MARRIED_MAXIMUM);
    (...)
    applyTax(theSalaries, TAXES.TAX_TYPE_3);
    (...)
}

public void applyTax(List<Salary> theSalaries, Tax taxToApply) {
    for(Salary aSalary : theSalaries) {
        aSalary.setAmount(calculateAmountWithTax(aSalary.getAmount(), taxToApply);
    }
}

(...)
Run Code Online (Sandbox Code Playgroud)

我需要的是处理这个工资集合,改变金额,但保留金额的所有中间"状态",在表格中向用户显示如下列:

示例报告:( 问题是关于前4行数据,不注意其余部分)

报告示例http://img198.imageshack.us/img198/5651/ue5l.jpg

我的想法:

在类Salary上为每个"部分结果"添加一个属性

public class Salary {
    (...)
    private BigDecimal originalAmount;
    private BigDecimal amountAfterFirstTax;
    private BigDecimal amountAfterMaximumRestriction;
    (...)
}
Run Code Online (Sandbox Code Playgroud)

问题:

  • "步骤"并不严格,明天可能会有一个"步骤"发生变化,出现新的步骤,或者某些步骤变化的"含义".在这种情况下,我需要过多地重构代码.
  • 一些"步骤"可以重复,那么,我怎样才能告诉"方法"哪个属性必须"设置"计算结果?

将HashMap添加到Salary类,我可以在其中放置部分结果,并将"key"传递给"step",它必须放置部分结果

public class Salary {
    (...)
    HashMap<String, BigDecimal> partialResults;
    (...)
}
Run Code Online (Sandbox Code Playgroud)

问题:

  • 在某些地方,我需要将HashMap填充到JPA实体以将其保存在我的数据库中
  • 如果另一个开发人员更改了密钥的名称(无论出于何种原因),则属性的"填充"可能会被破坏

最后注意:在我的应用程序中还有其他"类似"实体的其他类似情况,所以如果我们能找到一个通用的解决方案,那将是很好的:D


编辑:关于如何建模数据以保持持久性的新疑点

所有的想法都很相似,非常有用.所有这些都与命令模式有关,我认为这是很好的解决方案.但现在我有了一些新的疑虑:

通过更高级别的抽象,我的应用程序执行以下操作:

  1. 用户输入工资列表
  2. 应用程序通过不同的"步骤"处理此工资
  3. 使用最后工资金额计算平均值并继续其他计算
  4. 在一些屏幕和一些处理之后,我向FINAL工资金额和所有中间步骤向用户显示报告.然后我保存这些中间信息用于审计目的

所以,第二步几乎解决了.我会使用类似命令模式的东西.我现在的问题是如何建模数据以将其持久保存在关系数据库中.因为除了每个操作的部分"结果"之外,还有更多的信息我不需要向用户显示,但我需要将它存储在数据库中.

我的想法是这样的:

薪金表:

id
date // The date of the Salary
finalAmount // The final amount after all the calculations ends
Run Code Online (Sandbox Code Playgroud)

partialResults表:

id
salaryId // The id of the salary which this element represent a partial result
amount // Partial amount
operationCode // Type of operation
operationDescription 
Run Code Online (Sandbox Code Playgroud)

但问题是,我的"未来价值"操作作为输出具有以下信息:

  • 未来价值的日期
  • 用于更新值的系数
  • 部分金额

但"应用税"操作有不同的信息:

  • %税
  • 部分金额

那么,如何为不同的操作保存不同的"输出"信息呢?

Idi*_*olo 1

您可以创建一个名为SalaryReport的类,它由SalaryReportRow的许多对象组成。

SalaryReport中,您有一个方法名称calculate (),它执行所有算法。SalaryReportRow可以专门用于满足您需求的多种类型的行。

class SalaryReport {

    private List<SalaryReportRow> rows;
    private List<SalaryCalculator> calculators;

    private HashMap<SalaryCalculator,List<SalaryReportRow>> results;

    ...

    public float getResults() {

    }

    public void applyCalculators(){
        for(SalaryCalculator s : calculators){
            results.put(s,s.execute(rows));
        }
    }

    public void setSalaryCalculators(List<SalaryCalculator> calculators){
        this.calculators = calculators;
    }

    public float getCalculation(SalaryCalculator s){
            return results.get(s);
    }
    ...

}
Run Code Online (Sandbox Code Playgroud)

在计算税金时,您可以使用策略模式来使您的代码更加灵活。如果您必须结合许多策略,您也可以使用带有策略的复合模式或带有策略的责任链

abstract class SalaryCalculator implements Calculator {

        //Order of children matters
    protected List<SalaryCalculator> children = new ArrayList<SalaryCalculator>;

    public List<SalaryReportRow> execute(List<SalaryReportRow> rows){

        List<SalaryReportRow> result_rows = new ArrayList<SalaryReportRow> (rows);

        for(SalaryCalculator s: children){
            result_rows = s.execute(result_rows);
        }

        return result_rows;
    }

}

class TaxCalculator extends SalaryCalculator {

    public List<SalaryReportRow> execute(List<SalaryReportRow> rows){
        List<SalaryReportRow> result_rows = super.execute(rows);

        //
    }

}


class MaxSalaryCalculator extends SalaryCalculator {

    public List<SalaryReportRow> execute(List<SalaryReportRow> rows){
        List<SalaryReportRow> result_rows = super.execute(rows);

        ...
    }

}

class MaxSalaryWithTaxCalculator extends SalaryCalculator {

    public MaxSalaryWithTaxCalculator() {
        children.add(new MaxSalaryCalculator());
        children.add(new TaxCalculator());
    }

    public List<SalaryReportRow> execute(List<SalaryReportRow> rows){
        List<SalaryReportRow> result_rows = super.execute(rows);

        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,SalaryReport将有一个名为setSalaryCalculators(List)的方法(您也可以同时使用多个算法),其中 List 是包含所有应用的策略的列表。

您将有更多SalaryReportRows

  1. 一张是用来报税的。
  2. 一是为了赚到的钱。
  3. ...

这样您就可以轻松计算所有步骤。

当您与存储处理程序通信时,我建议使用带有负责创建和保存对象的接口的DAO 模式。

我希望这会有所帮助。:)

  • 有件事你误解了,我不需要部分值的总和,我需要的是金额的不同部分“状态”。例如:原始金额:100。含税金额:110。受最大限制的含税金额:90。受第二个税额限制的含税金额:95。应用程序使用最后一个值进行另一次计算,比如均值,等等。但对于我的应用程序的最后一个“屏幕”,我必须向用户显示计算的详细信息。想象一种电子表格,其中包含每个“步骤”的详细信息以获取最后的金额 (2认同)