2010-01-16 87 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>