白愛民 廊坊師範學院資訊科技提高班十四期
什麼是“介面卡”? |
小白簡介: 我想要一個三頭插座的充電器但是現在的插排都兩頭的插座所以我想要一個連線三頭插座的線,那麼這個使用過程就是介面卡模式,其中需要的三頭插座就是適配類。
介面卡模式(Adapter): 將一個類的介面轉換成客戶希望的另一個介面。Adapter模式使得原本由於介面不相容而不能一起工作的那些類可以一起工作。(是將原本有的介面程式設計客戶需要的介面,如同下面的類圖中:將進攻和防守的英文方法改為中文方法,這樣外籍中鋒就懂了)
此模式為物件介面卡模式,介面卡分為類介面卡和物件介面卡,類介面卡通過對重繼承對一個介面與另一個介面進行匹配,但是C#不支援多重繼承,所以今天講物件介面卡。
首先我們需要先設定球員介面和前鋒、中鋒、後衛類
/// <summary>
/// 球員類
/// </summary>
abstract class Player
{
protected string name;
public Player(string name)
{
this.name = name;
}
//進攻和防守方法
public abstract void Attack();
public abstract void Defense();
}
//前鋒
class Forwards:Player
{
//此方法是直接訪問父類
public Forwards(string name)
: base(name)
{
}
public override void Attack()
{
Console.WriteLine("前鋒{0} 進攻", name);
}
public override void Defense ()
{
Console.WriteLine("前鋒{0} 防守", name);
}
}
//中鋒
class Center:Player
{
//此方法是直接訪問父類
public Center(string name)
: base(name)
{
}
public override void Attack()
{
Console.WriteLine("前鋒{0} 進攻", name);
}
public override void Defense()
{
Console.WriteLine("前鋒{0} 防守", name);
}
}
//後衛
class Guards:Player
{
//此方法是直接訪問父類
public Guards(string name)
: base(name)
{
}
public override void Attack()
{
Console.WriteLine("前鋒{0} 進攻", name);
}
public override void Defense()
{
Console.WriteLine("前鋒{0} 防守", name);
}
}
接著我們書寫外籍中鋒和翻譯者類:
/// <summary>
/// 外籍中鋒
/// </summary>
class ForeignCenter
{
private string name;
public string Name
{
//外籍中鋒類球員的姓名故意用屬性而不是構造方法來區別於前三個球員類的不同
get { return name; }
set { name = value; }
}
//以下標明“外籍中鋒"只懂得中文“進攻” 和“防守”
public void 進攻()
{
Console.WriteLine("外籍中鋒{0} 進攻", name);
}
public void 防守()
{
Console.WriteLine("外籍中鋒{0} 防守", name);
}
}
/// <summary>
/// 翻譯者類
/// </summary>
class Translator:Player
{
private ForeignCenter wjzf = new ForeignCenter();
public Translator(string name)
: base(name)
{
wjzf.Name = name;
}
public override void Attack()
{
wjzf.進攻();
}
public override void Defense()
{
wjzf.防守(); }
}
方法總結: 前面的球員和幾個身份都是我們一致的介面和實現介面,然後外籍中鋒的名字為了區別和其他不同所以自己構造一個姓名方法,接著方法名是中文的,在翻譯者類中只是重寫了球員介面的方法,將裡面改為外籍中鋒的方法。,然後在客戶端請看:
static void Main(string[] args)
{
Player b = new Forwards("巴塞爾");
b.Attack();
Player m = new Guards("麥克地雷地");
m.Attack();
//翻譯者告訴姚明,教練要求你既要“進攻” 又要“防守”
Player ym = new Translator("姚明");
ym.Attack();
ym.Defense();
Console.Read();
}
執行結果:
接下來請看橋接模式 |
首先我們先了解一下合成/聚合複用原則(CARP),儘量使用合成/聚合原則,儘量不要使用類繼承:因為使用CARP有助於保持每個類被封裝,並被集中在單個任務上,這樣類和類繼承會保持較小規模,說白了就是手機品牌應該包含手機軟體,但是軟體並不是手機品牌的一部分,,這樣就不會將全部的資訊都放在手機品牌裡面,大大減少了耦合,讓其分離出來。
橋接模式: 將抽象部分與它的實現部分分離,使他們都可以獨立的變化。(就是讓抽象類和派生類分離,原本手機軟體是手機的一部分,現在讓手機軟體分離出來),請看類圖關係:
首先寫出我們的手機軟體介面和兩個手機軟體
/// <summary>
/// 手機軟體介面
/// </summary>
abstract class Soft
{
public abstract void Run();
}
/// <summary>
/// 手機遊戲
/// </summary>
class Game: Soft
{
public override void Run()
{
Console.WriteLine("手執行機遊戲");
}
}
/// <summary>
/// 手機音樂
/// </summary>
class Music : Soft
{
public override void Run()
{
Console.WriteLine("執行手機音樂");
}
}
接著我們寫手機品牌介面和兩個手機型別
/// <summary>
/// 手機品牌介面
/// </summary>
abstract class Phone
{
protected Soft soft; //連線手機軟體
public void SetSoft(Soft soft)
{
this.soft=soft;
}
public abstract void Run();
}
/// <summary>
/// 手機品牌:Nokia
/// </summary>
class Nokia : Phone
{
public override void Run()
{
soft.Run();
}
}
/// <summary>
/// 手機品牌:Winphone
/// </summary>
class WinPhone:Phone
{
public override void Run()
{
soft.Run();
}
}
方法總結: 在我們的類中只寫一個手機品牌的介面和手機軟體的介面,並且去分別實現他們不同的子類,因為手機品牌和軟體是聚合關係,所以需要在軟體介面那裡做一個連線。 接著看客戶端:
static void Main(string[] args)
{
//例項化一個手機品牌Nokia,然後給他遊戲和音樂的方法
Phone ab = new Nokia(); ;
ab.SetSoft(new Game());
ab.Run();
ab.SetSoft(new Music());
ab.Run();
//例項化一個手機品牌WinPhone,然後給他音樂和遊戲的方法
Phone cd = new WinPhone();
cd.SetSoft(new Game());
cd.Run();
cd.SetSoft(new Music());
cd.Run();
Console.Read();
}
執行結果:
小結 |
相同點: 兩者都是結構型設計模式,採用繼承機制來組合介面或實現。
不同點: 介面卡模式是將一個類的介面轉換成客戶希望的另一個介面。而橋接是將抽象部分與其實現部分分離,使它們都可以獨立的變化。
小結: 更多的不同點和區別已經在程式碼和說明處給出,這樣寫的好處是更加深刻的體會出設計模式的細節所在,更多的還需要我們共同討論出來哦。