Design Pattern - Factory Method

(史帝芬, 2007/05/13, hi.steven@gmail.com)
  • 前言

  •     某家銀行有兩家分行,分別為台灣分行及香港分行,每家分行裡有三 種職位的人 -- Manager、Team Leader、Employee,現在要設計一套系統,用來印出薪水條, 在考量這家分行在不久的將來將於中國及英國設分行,系統應保留於設立中國分行、英國分行時, 只需加入新的有關中國、英國分行薪水計算的程式,不需修改既有程式,程式應如何設計?
        這裡將以Factory Method Pattern來設計這套系統,而且會有許多不 同的實作方式,讓讀者了解一下,同樣的Pattern在不同的思考模式下,也會不同的型式。
  • Class Diagram


  • 程式1 (C#)

  • using System;
    
    namespace Factory
    {
        public abstract class Bank
        {
            public PayList create(string position)
            {
                PayList p = createPayList(position);
                return p;
            }
    
            protected abstract PayList createPayList(string position);
        }
    
        public class TaiwanBranch : Bank
        {
            protected override PayList createPayList(string position) 
            {
                switch (position)
                {
                    case "Manager":
                        return new TaiwanManagerPayList();
                    case "TeamLeader":
                        return new TaiwanTeamLeaderPayList();
                    default:
                        return new TaiwanEmploiyeePayList();
                }
            }
        }
    
        public class HongKongBranch : Bank
        {
            protected override PayList createPayList(string position)
            {
                return new HongKongManagerPayList();
            }
        }
    
        public abstract class PayList
        {
            protected double basePay;
            protected double bonus;
            protected double taxRating;
    
            public abstract double revenue();
    
            public abstract double tax();
    
            public abstract double paying();
    
            public void printPayList()
            {
                Console.WriteLine("薪水: {0}", revenue());
                Console.WriteLine("所得稅: {0}", tax());
                Console.WriteLine("應付: {0}", paying());
            }
        }
    
        public class TaiwanManagerPayList : PayList
        {
            public TaiwanManagerPayList()
            {
                basePay = 80000;
                bonus = 20000;
                taxRating = 0.21;
            }
    
            public override double revenue()
            {
                return basePay + bonus;
            }
    
            public override double tax()
            {
                return revenue() * taxRating;
            }
    
            public override double paying()
            {
                return revenue() - tax();
            }
        }
    
        public class TaiwanTeamLeaderPayList : PayList
        {
            public TaiwanTeamLeaderPayList()
            {
                basePay = 60000;
                bonus = 3000;
                taxRating = 0.13;
            }
    
            public override double revenue()
            {
                return basePay + bonus;
            }
    
            public override double tax()
            {
                return revenue() * taxRating;
            }
    
            public override double paying()
            {
                return revenue() - tax();
            }
        }
    
        public class TaiwanEmploiyeePayList : PayList
        {
            public TaiwanEmploiyeePayList()
            {
                basePay = 40000;
                bonus = 0;
                taxRating = 0.06;
            }
    
            public override double revenue()
            {
                return basePay + bonus;
            }
    
            public override double tax()
            {
                return revenue() * taxRating;
            }
    
            public override double paying()
            {
                return revenue() - tax();
            }
        }
    
        public class HongKongManagerPayList : PayList
        {
            public HongKongManagerPayList() 
            {
                basePay = 100000;
                bonus = 30000;
                taxRating = 0.32;
            }
    
            public override double revenue()
            {
                return basePay + bonus;
            }
    
            public override double tax()
            {
                return basePay * taxRating;
            }
    
            public override double paying()
            {
                return revenue() - tax();
            }
        }
    
        public class HongKongTeamLeaderPayList : PayList
        {
            public HongKongTeamLeaderPayList()
            {
                basePay = 68000;
                bonus = 12000;
                taxRating = 0.20;
            }
    
            public override double revenue()
            {
                return basePay + bonus;
            }
    
            public override double tax()
            {
                return basePay * taxRating;
            }
    
            public override double paying()
            {
                return revenue() - tax();
            }
        }
    
        public class HongKongEmployeePayList : PayList
        {
            public HongKongEmployeePayList()
            {
                basePay = 50000;
                bonus = 0;
                taxRating = 0.09;
            }
    
            public override double revenue()
            {
                return basePay + bonus;
            }
    
            public override double tax()
            {
                return basePay * taxRating;
            }
    
            public override double paying()
            {
                return revenue() - tax();
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                Bank twBranch = new TaiwanBranch();
    
                Console.WriteLine("Manager");
                PayList twManager = twBranch.create("Manager");
                twManager.printPayList();
    
                Console.WriteLine("Team Leader");
                PayList twTeamLeader = twBranch.create("TeamLeader");
                twTeamLeader.printPayList();
    
                Console.WriteLine("Employee");
                PayList twEmployee = twBranch.create("Employee");
                twEmployee.printPayList();
    
                Console.ReadLine();
            }
        }
    }
    

  • 說明1

  •     未來增加中國分行時,程式只要增加ChinaBranch、ChinaManagerPayList、 ChinaTeamLeaderPayList、ChinaEmployeePayList四個class即可,不需修改到既有程式。
        上面的程式有個問題,就是programmer需了解Bank及PayList兩個介面,如 果我們希望programmer只要了解Bank這個介面,可以將class Bank改寫如下,Bank合成PayList, 提供外界所需的介面,這樣programmer只要透過class Bank就可以完成所有需要的動作了。
  • 程式2 (C#)

  •     public abstract class Bank
        {
            private PayList payList;
    
            public void create(string position)
            {
                payList = createPayList(position);
            }
    
            protected abstract PayList createPayList(string position);
    
            public double revenue()
            {
                return payList.revenue();
            }
    
            public double tax()
            {
                return payList.tax();
            }
    
            public double paying()
            {
                return payList.paying();
            }
    
            public void printPayList()
            {
                payList.printPayList();
            }
        }
    
    …
    
        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine("Manager");
                Bank twBranch = new TaiwanBranch();
                twBranch.create("Manager");
                twBranch.printPayList();
    
                Console.WriteLine("Team Leader");
                twBranch.create("TeamLeader");
                twBranch.printPayList();
    
                Console.WriteLine("Employee");
                twBranch.create("Employee");
                twBranch.printPayList();
    
                Console.ReadLine();
            }
        }
    

  • 說明2

  •     第2個程式穩藏了PayList的實作細節,讓Bank只要操作PayList這個介面 即可得到想要的結果,在實際應用中,到底要用第一或第二種方式,是根據需求而定,並沒有絕對 的優劣之分。