Delegate Pattern

目錄

一.Delegate Pattern簡介

二.Delegate Pattern之理念

三.Delegate Pattern的組成

四.Delegate Pattern實作

五.Delegate Pattern在IOS上的哪些地方被使用

六.Delegate Pattern的好處

七.參考資料

一.Delegate Pattern簡介

Delegate Pattern 是一種設計模式,使類別和結構能夠將其某些職責委託給其他物件完成。

二.Delegate Pattern之理念

透過委託模式將委託物件中某些職責交由被委託物件完成,關鍵在於被委託的物件不需要了解委託物件的實現細節,只需要負責實現委託協議內的方法,這種作法可以有效降低委託物件及被委託物件的耦合。

三.Delegate Pattern組成

1 . 委託物件 (the delegator)

2 . 委託協議 (the delegate protocol)

3 . 被委託物件 (the delegate)

四.Delegate Pattern實作

1 . 委託物件 (the delegator)

委託物件需要宣告一個委託協議的變數,並調用該委託協議中的方法。

為了避免循環引用造成記憶體洩漏,在宣告委託協議變數時”必須”加上weak修飾詞。

class People{
    // 加上weak修飾詞防止記憶體洩漏
    var food:String = "";
    var drink:String="";
    weak var delegate:PeopleDelegate?
    func buyDinner(){
        //調用被委託物件中委託協議之方法
        delegate?.buyDinner(food,drink);
    }
}

2 . 委託協議(protocol)

將需要由被委託物件完成之方法寫入協議(protocol)中

protocol Peopleprotocol PeopleDelegate: AnyObject {
    func buyDinner(_ dinner: String,_ drink:String);
}
extension PeopleDelegate{//使用extension讓方法可以變成可選擇是否實作
    func orderDrink()->String{return "";}
}

✱因為委託物件在宣告委託協議的變數使用了weak修飾詞,而weak修飾詞只能在變數為參考型別時使用,為了避免因為不確定實現此委託協議的被委託物件為何種型別而造成編譯器報錯,此時需要將此委託協議繼承AnyObject,如此一來即可確定實現此委託協議的類別必為參考型別

✱撰寫protocol的要點

  • 為了明確說明方法確實是委託方法,通常的做法是使用要委託的類型的名稱作為方法名稱的開頭。Ex:tableView
  • 理想情況下,委託方法的第一個參數應該是委託對象本身。在委託對象擁有多個實體時方便區別。
  • 委託時,重要的是不將任何委託物件的實現細節洩漏給被委託之物件。

3 . 被委託物件 (the delegate)

被委託物件將實現協議(protocol) 並實作協議裡的方法

實現delegate protocol的類別(稱為 delegate)能夠提供被委託的方法

class FoodPanda:PeopleDelegate{
    func buyDinner(_ dinner: String,_ drink:String){
        if(drink.isEmpty){
            print("FoodPanda將\(dinner)送到你家門口")
        }else{
            print("FoodPanda將\(dinner)\(drink)送到你家門口")
        }
    }
}
class Ubereats:PeopleDelegate{
    func buyDinner(_ dinner: String,_ drink:String) {
        if(drink.isEmpty){
            print("Ubereats將\(dinner)送到你家樓下")
        }else{
            print("Ubereats將\(dinner)\(drink)送到你家樓下")
        }
    }
}

實際運行

let people = People();

let foodPanda = FoodPanda();

let ubereats = Ubereats();

people.food = "肯德基";
people.delegate = foodPanda;

people.buyDinner();

people.food = "牛肉麵";
people.drink = "50嵐";

people.delegate = ubereats;

people.buyDinner();

輸出結果

五.Delegate Pattern在IOS上的哪些地方被使用

  • UIApplicationDelegate

  • UITableViewDelegate

  • UITableViewDataSource

  • UITextFieldDelegate

  • UISearchBarDelegate

  • UICollectionViewDelegate

  • UICollectionViewDataSource

    …等等還有許多元件與開發套件都使用Delegate Pattern,使用者只需要實作協議中的方法即可使用。

實際使用UITableViewDelegate,UITableViewDataSource範例

class NBATeam{
    var teamName:String?;
    var players:[String]?;
    
    init(teamName:String,players:[String]) {
        self.teamName = teamName;
        self.players = players;
    }
}

class ViewController: UIViewController {
    
    @IBOutlet weak var tableView: UITableView!
    var nbaTeams = [NBATeam]();
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        nbaTeams.append(NBATeam.init(teamName: "Lakers", players: ["james","davids","gasol"]));
        nbaTeams.append(NBATeam.init(teamName: "Celtics", players: ["smart","tatum","jaylon","walker"]));
        nbaTeams.append(NBATeam.init(teamName: "Clippers", players: ["leonard","george"]));
        nbaTeams.append(NBATeam.init(teamName: "Bucks", players: ["antetokounmpo","bogdanovic"]));
        nbaTeams.append(NBATeam.init(teamName: "Nets", players: ["durant","irving"]));
        nbaTeams.append(NBATeam.init(teamName: "Mavericks", players: ["doncic","porzingis"]));
        nbaTeams.append(NBATeam.init(teamName: "Blazers", players: ["lillard","mcCollum"]));
    }
}
extension ViewController:UITableViewDelegate,UITableViewDataSource{
    //  決定section數量
    func numberOfSections(in tableView: UITableView) -> Int {
        return nbaTeams.count;
    }
    //決定每個section會有幾筆資料(UITableViewDataSource必須實現的方法)
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return nbaTeams[section].players?.count ?? 0;
    }
    // 設定每筆資料內容(UITableViewDataSource必須實現的方法)
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell",for: indexPath);
        cell.textLabel?.text = nbaTeams[indexPath.section].players?[indexPath.row];
        return cell;
    }//設定section title
    func tableView(_ tableView: UITableView,
      titleForHeaderInSection section: Int) -> String? {
        return nbaTeams[section].teamName;
    }
    //設置footer
    func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
        let view = UIView(frame: CGRect(x: 0, y: 0, width: tableView.frame.width, height: 20));
        view.backgroundColor = .blue;//設定view底色
        let label = UILabel(frame: CGRect(x: 15, y: 0, width: view.frame.width-15, height: 20));
        view.addSubview(label);將label加入view中
        label.text = "---------------------------------------";//設定label內容
        label.textColor = .white;//設定label字的顏色
        return view;
    }
    //設置footer高度
    func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
        return 20;
    }
}

六.Delegate Pattern的好處

  • 不需要複雜的程式碼即可將任務從一個物件傳遞到另一個物件。
  • 只需要一個協議就可以在類別間傳遞需求或是資料,可以很好的降低耦合。
  • 容易的將特定職責從物件中抽出。
  • 在套件開發中可以很好的聯繫套件使用者的程式碼,並在套件使用上更加直覺。

七.參考資料