内容简介:Decorator模式允许通过使用多个嵌套层包装它来动态扩展现有对象的功能。所有这些层必须实现相同的接口,这样才能组合它们。让我们用一个实际的例子证明这一点:我们需要从年薪总额开始计算工资计算器,并在将其除以12并对其征收一系列税后计算每月净工资。为方便起见,有关这些税收的业务逻辑已经分组在一组静态方法中,每个方法都实现了给定税的应用。实施必须足够灵活,才能允许从薪资计算算法中动态添加和删除税。这意味着每个层执行的特定计算可以建模为double的简单转换为另一个double。我们计算的每个阶段都必须实现以下
Decorator模式允许通过使用多个嵌套层包装它来动态扩展现有对象的功能。所有这些层必须实现相同的接口,这样才能组合它们。
让我们用一个实际的例子证明这一点:我们需要从年薪总额开始计算工资计算器,并在将其除以12并对其征收一系列税后计算每月净工资。为方便起见,有关这些税收的业务逻辑已经分组在一组静态方法中,每个方法都实现了给定税的应用。
<b>public</b> <b>class</b> Taxes { <b>public</b> <b>static</b> <b>double</b> generalTax(<b>double</b> salary) { <b>return</b> salary * 0.8; } <b>public</b> <b>static</b> <b>double</b> regionalTax(<b>double</b> salary) { <b>return</b> salary * 0.95; } <b>public</b> <b>static</b> <b>double</b> healthInsurance(<b>double</b> salary) { <b>return</b> salary - 200.0; } }
实施必须足够灵活,才能允许从薪资计算算法中动态添加和删除税。这意味着每个层执行的特定计算可以建模为double的简单转换为另一个double。我们计算的每个阶段都必须实现以下接口:
<b>interface</b> SalaryCalculator { <b>double</b> calculate(<b>double</b> grossAnnual); }
正如预期的那样,工资计算过程的第一阶段是按12除,从年度工资开始计算月工资。
<b>public</b> <b>class</b> DefaultSalaryCalculator implements SalaryCalculator { @Override <b>public</b> <b>double</b> calculate(<b>double</b> grossAnnual) { <b>return</b> grossAnnual / 12; } }
现在有必要建立一种机制,将第一个SalaryCalculator实施与将应用上述税收的其他实施相结合。为此,我们可以使用Decorator模式,将其本质封装到抽象类中。
<b>public</b> <b>abstract</b> <b>class</b> AbstractTaxDecorator implements SalaryCalculator { <b>private</b> <b>final</b> SalaryCalculator salaryCalculator; <b>public</b> AbstractTaxDecorator( SalaryCalculator salaryCalculator ) { <b>this</b>.salaryCalculator = salaryCalculator; } <b>protected</b> <b>abstract</b> <b>double</b> applyTax(<b>double</b> salary); @Override <b>public</b> <b>final</b> <b>double</b> calculate(<b>double</b> gross) { <b>double</b> salary = salaryCalculator.calculate( gross ); <b>return</b> applyTax( salary ); } }
这个类装饰(包装)一个SalaryCalculator,但它本身也是一个SalaryCalculator。它有一个抽象方法,该类的每个具体实现都必须提供其特定的业务逻辑。通过这种方式,这个抽象类可以直接实现calculate()方法,方法是将总工资传递给它包装的SalaryCalculator,然后将自己的计算函数应用于结果。在开发了这种抽象之后,现在可以为我们可能希望最终应用于总薪水的每种税收使用前抽象类的不同实现。特别是,我们将实施适用一般税收的实施:
<b>public</b> <b>class</b> GeneralTaxDecorator <b>extends</b> AbstractTaxDecorator { <b>public</b> GeneralTaxDecorator( SalaryCalculator salaryCalculator ) { <b>super</b>( salaryCalculator ); } @Override <b>protected</b> <b>double</b> applyTax(<b>double</b> salary) { <b>return</b> Taxes.generalTax( salary ); } }
另一个是区域性的:
<b>public</b> <b>class</b> RegionalTaxDecorator <b>extends</b> AbstractTaxDecorator { <b>public</b> RegionalTaxDecorator( SalaryCalculator salaryCalculator ) { <b>super</b>( salaryCalculator ); } @Override <b>protected</b> <b>double</b> applyTax(<b>double</b> salary) { <b>return</b> Taxes.regionalTax( salary ); } }
第三个涵盖健康保险:
<b>public</b> <b>class</b> HealthInsuranceDecorator <b>extends</b> AbstractTaxDecorator { <b>public</b> HealthInsuranceDecorator( SalaryCalculator salaryCalculator ) { <b>super</b>( salaryCalculator ); } @Override <b>protected</b> <b>double</b> applyTax(<b>double</b> salary) { <b>return</b> Taxes.healthInsurance( salary ); } }
最后,我们现在准备用以前实现的所有或部分装饰器组合第一个DefaultSalaryCalculator,并将生成的年薪转移到生成的SalarayCalculator。
<b>double</b> netSalary = <b>new</b> HealthInsuranceDecorator( <b>new</b> RegionalTaxDecorator( <b>new</b> GeneralTaxDecorator( <b>new</b> DefaultSalaryCalculator() ) ) ).calculate( 30000.00 ));
函数式实现
原始的DefaultSalaryCalculator只是一个将double转换为另一个double的函数。为了避免任何不必要的原始double的装箱和拆箱,我们可以使DefaultSalaryCalculator实现DoubleUnaryOperator而不是普通的Function。
<b>public</b> <b>class</b> DefaultSalaryCalculator implements DoubleUnaryOperator { @Override <b>public</b> <b>double</b> applyAsDouble(<b>double</b> grossAnnual) { <b>return</b> grossAnnual / 12; } }
此外,分组到Taxes类中的所有其他静态方法都具有相同的签名,然后可以将其视为DoubleUnaryOperator接口的其他实现。现在是时候想知道Decorator模式的本质是什么了。实际上它提供了一种组合不同计算的方法,但函数组合当然是函数式编程中更自然的东西。令人惊讶的是,我们可以获得完全相同的结果,这使我们花费了大量精力使用Decorator模式,如下所示:
<b>double</b> netSalary = <b>new</b> DefaultSalaryCalculator() .andThen( Taxes::generalTax ) .andThen( Taxes::regionalTax ) .andThen( Taxes::healthInsurance ) .applyAsDouble( 30000.00 );
请注意,此习惯用法允许按照与基于Decorator的实现相同的方式按需添加和删除功能。而且,这次计算以与写入函数相同的顺序进行。还可以为我们的用户提供更好的API,以实现接受总薪水的方法以及要应用于其的函数的变量。
<b>public</b> <b>static</b> <b>double</b> calculate(<b>double</b> gross, DoubleUnaryOperator... fs) { <b>return</b> Stream.of( fs ) .reduce( DoubleUnaryOperator.identity(), DoubleUnaryOperator::andThen ) .applyAsDouble( gross ); }
这里通过将varargs数组中的所有函数放入Stream并将函数Stream简化为单个函数来实现函数组合。现在可以通过这种方式调用这个新的静态方法来计算净工资。
<b>double</b> netSalary = calculate( 30000.00, <b>new</b> DefaultSalaryCalculator(), Taxes::generalTax, Taxes::regionalTax, Taxes::healthInsurance );
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 使用函数式实现观察者模式模式
- 设计模式之发布订阅模式(2) Redis实现发布订阅模式
- 设计模式:创建型模式之单例模式的五种实现
- 实践:使用Spring 原生注解来快速实现 策略模式 + 工厂模式
- 如何实现Builder模式
- 策略模式-Golang实现
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
ggplot2:数据分析与图形艺术
哈德利·威克姆 (Hadley Wickham) / 统计之都 / 西安交通大学出版社 / 2013-5-1 / CNY 46.00
中译本序 每当我们看到一个新的软件,第一反应会是:为什么又要发明一个新软件?ggplot2是R世界里相对还比较年轻的一个包,在它之前,官方R已经有自己的基础图形系统(graphics包)和网格图形系统(grid包),并且Deepayan Sarkar也开发了lattice包,看起来R的世界对图形的支持已经足够强大了。那么我们不禁要问,为什么还要发明一套新的系统? 设计理念 打个比......一起来看看 《ggplot2:数据分析与图形艺术》 这本书的介绍吧!