2010-01-16 85 views
4

如果您在一个团队中,并且程序员为您提供了创建,读取,更新和删除方法的界面,您如何避免类型切换?避免类型切换

报价敏捷软件工艺的清洁守则的手册:

public Money calculatePay(Employee e) 
    throws InvalidEmployeeType { 
     switch (e.type) { 
      case COMMISSIONED: 
       return calculateCommissionedPay(e); 
      case HOURLY: 
       return calculateHourlyPay(e); 
      case SALARIED: 
       return calculateSalariedPay(e); 
      default: 
       throw new InvalidEmployeeType(e.type); 
    } 
} 

有几个问题与此功能。首先,它很大,并且当添加新的雇员类型时,它将会增长。其次,它显然不止一件事。第三,它违反了单一责任原则7(SRP),因为有多个原因需要改变。第四,它违反了开放封闭原则8(OCP),因为每当添加新类型时它必须改变。但是这个 函数可能最糟糕的问题是,有无限数量的其他函数将具有相同的 结构。例如,我们可以有

isPayday(Employee e, Date date), 

deliverPay(Employee e, Money pay), 

或他人的主机。所有这些都会有相同的有害结构。

这本书告诉我使用工厂模式,但它的方式让我觉得我不应该使用它。

再次引用这本书:

的解决了这个问题(见列表3-5)是埋藏在地下 抽象工厂,9 switch语句,绝不让任何人看到它。

开关语句是否丑陋?

+2

这不是PHP代码.... – 2010-01-16 21:33:18

+0

我删除了标签。 – 2010-01-16 21:42:35

+0

您可能想要接受答案。 – 2010-01-16 22:25:22

回答

7

实际上,雇员对象应该有自己的计算工资函数,它会给你工资。这个计算工资函数会根据员工的类型而变化。

这样就可以定义实现的对象,而不是对象的用户。

abstract class Employee 
{ 
    public abstract function calculatePay(); 
} 

class HourlyEmployee extends Employee 
{ 
    public function calculatePay() 
    { 
      return $this->hour * $this->pay_rate; 
    } 
} 

class SalariedEmployee extends Employee 
{ 
    public function calculatePay() 
    { 
      return $this->monthly_pay_rate; 
    } 
} 

当你建立工厂时,那么你在那里做switch语句,并且只有一次建立员工。

可以说,员工在一个阵列,和员工的类型是$array['Type']

public function buildEmployee($array) 
{ 
    switch($array['Type']){ 
     case 'Hourly': 
      return new HourlyEmployee($array); 
      break; 
     case 'Salaried': 
      return new SalariedEmployee($array); 
      break; 
} 

最后举行,计算出工资

$employee->calculatePay(); 

现在,有没有必要超过一个switch语句根据员工的类型来计算员工的工资。它只是员工对象的一部分。

免责声明,我是未成年人,所以我对这些付款中的一部分如何计算并不完全积极。但论据的基础仍然有效。应该在对象中计算工资。

免责声明2这是PHP代码。但是再一次,这个论点应该对任何语言都有效。

+0

你可能想扩展这些员工:) – Anurag 2010-01-16 21:38:37

+0

雅...我很讨厌在这个编辑器中打字:) – 2010-01-16 21:39:12

+0

是的..我真的希望一个标签实际上把一个选项卡,而不是去页面中的下一个元素 – Anurag 2010-01-16 21:41:58

1

我在某处读过它,如果您使用的是switch,那么可能会有太多变化。而当我们有太多变化时,我们应该尝试封装接口后面的变体,从而解耦对象之间的依赖关系。话虽如此,我认为你应该尝试创建一个轻量级的基类对象来封装这种类型的逻辑。然后你让它成为class Employee的成员,然后摆脱switch构造。这里就是我的意思,简而言之:

abstract class SalaryType 
{ 
    function calculatePay() {} 
} 

class CommissionedType extends SalaryType 
{ 
    function calculatePay() {}  
} 

class HourlyType extends SalaryType 
{ 
    function calculatePay() {}  
} 

class SalaryType extends SalaryType 
{ 
    function calculatePay() {}  
} 

class Employee 
{ 
    private $salaryType; 

    public function setType(SalaryType emp) 
    { 
    $this->salaryType = emp; 
    } 

    public function calculatePay() 
    { 
    $this->salaryType->calculatePay(); 
    } 
} 

顺便说一句,有很多你的示例代码似乎并不十分“PHP十岁上下”。 PHP中没有返回类型,也没有任何类型安全。请记住,PHP并不是真正的多态,所以在典型的类型安全语言中发现的一些多态行为可能无法按预期的方式工作。

+0

我想你错打了一些东西。 – 2010-01-16 22:24:42

+0

thx,修正它们... :) – Zoltan 2010-01-16 22:26:51

+0

$ private salaryType;应该仍然是private $ salaryType。 – sprugman 2010-01-16 22:38:41

2

您可以使用某种Map号完全删除该开关,以将员工类型映射到其相应的薪酬计算器。这取决于反思,并且可以用我所知道的所有语言。

假设工资计算不雇员的责任,我们有一个接口PayCalculation

interface PayCalculation { 
    function calculatePay(Employee $employee); 
} 

有员工的每个类别的实现:

class SalariedPayCalculator implements PayCalculation { 
    public function calculatePay(SalariedEmployee $employee) { 
     return $employee.getSalary(); 
    } 
} 

class HourlyPayCalculator implements PayCalculation { 
    public function calculatePay(HourlyEmployee $employee) { 
     return $employee.getHourlyRate() * e.getHoursWorked(); 
    } 
} 

class CommissionedPayCalculator implements PayCalculation { 
    public function calculatePay(CommissionedEmployee $employee) { 
     return $employee.getCommissionRate() * $employee.getUnits(); 
    } 
} 

而且工资计算将像这样工作。反射对于此来查看对象并在运行时确定它是类很重要。借此,可以消除开关回路。

public class EmployeePayCalculator implements PayCalculation { 

    private $map = array(); 

    public function __construct() { 
     $this->map['SalariedEmployee'] = new SalariedPayCalculator(); 
     $this->map['HourlyEmployee'] = new HourlyPayCalculator(); 
     $this->map['CommissionedEmployee'] = new CommissionedPayCalculator(); 
    } 

    public function calculatePay(Employee $employee) { 
     $employeeType = get_class($employee); 
     $calculator = $this->map[$employeeType]; 
     return $calculator->calculatePay($employee); 
    } 
} 

在这里,我们初始化构造函数中的地图,但它很容易被外移到一个XML配置文件或某些数据库:

<payCalculation> 
    <category> 
     <type>Hourly</type> 
     <payCalculator>HourlyPayCalculator</payCalculator> 
    </category> 
    <category> 
     <type>Salaried</type> 
     <payCalculator>SalariedPayCalculator</payCalculator> 
    </category> 
    ... 
</payCalculation>