兩者無優劣,唯適用場景不同。
劉國梁:選手 → 教練。用介面 IPlayer、ICoach 建模:
IPlay、ITeach,物件持有行為屬性,透過屬性賦值/拋異常表達「擁有/不擁有」某行為// 繼承版:靜態、僵化
interface IPlayer { void Play(); }
interface ICoach { void Teach(); }
class LiuGuoLiang : IPlayer, ICoach { ... } // 一輩子同時是選手與教練?
// 組合版:動態行為
class Person
{
public IPlay PlayBehavior { get; set; }
public ITeach TeachBehavior { get; set; }
public void Play() => PlayBehavior?.Play() ?? throw new InvalidOperationException("非選手");
public void Teach() => TeachBehavior?.Teach() ?? throw new InvalidOperationException("非教練");
}
// 使用
var liu = new Person { PlayBehavior = new PlayerBehavior() };
liu.Play(); // 選手時期
liu.PlayBehavior = null;
liu.TeachBehavior = new CoachBehavior();
liu.Teach(); // 教練時期
組合將焦點從「繼承什麼」轉移到「能做什麼」,雖 C# 組合關係仍為靜態,但已引入動態元素(屬性可變),更貼近領域本質。
下篇將展示無需介面實作、無需組合屬性,直接以行為簽名動態粘合物件與介面的 Duck Typing 技術。