马儿三好:如何理解适配器模式Adapter Pattern(用C#语言)

适配器模式是设计模式中比较重要的一个,适配器模式的重要意义在于接口转换,以此达到复用的功效,而两个系统却仍独立发展,互不相干。所以适配器,不可滥用。

 

 

我们用无名的音频播放器的发展来理解适配器Adapter模式。

 

1、最初的播放音频代码(播放技术与应用)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Study_DesignPattern
{
    /*
     * 播放技术的应用,用户创建的播放列表
     */
    public class PlayerListClient
    {
        private List<IPlayer> Players = new List<IPlayer>();
        public void AddPlayer(IPlayer player) { this.Players.Add(player); }
        public void Play()
        {
            foreach (var player in this.Players)
                player.play();
        }
    }
    /*
     * 这是第一代播放技术
     * 共计:2500行
     */
    public interface IPlayer { void play(); }
    public class WavPlayer : IPlayer
    {
        private string Music_Path { get; set; }
        public WavPlayer(string music_path) { this.Music_Path = music_path; }
        public void Play()
        {
            Console.WriteLine(String.Format("{0} starts playing!", this.Music_Path));
            //此处略去1000行
        }
    }
    public class WmvPlayer : IPlayer
    {
        private string Music_Path { get; set; }
        public WmvPlayer(string music_path) { this.Music_Path = music_path; }        
        public void Play()
        {
            Console.WriteLine(String.Format("{0} starts playing!", this.Music_Path));
            //此处略去1500行
        }
    }
}

  2、又过了几年,Mp3兴起来了。用户的磁盘上mp3多了起来,而我们的播放器不能播放。很简单,那就添加一个Mp3Player吧。

    public class Mp3Player : IPlayer
    {
        private string Music_Path { get; set; }
        public Mp3Player(string music_path) { this.Music_Path = music_path; }
        public void Play()
        {
            Console.WriteLine(String.Format("{0} starts playing!", this.Music_Path));
            //预测需要3000行
        }
    }

  要是前几年,公司大拿多的是,三天就写出来了,我也想文章也就终止了。

       3、可是如今技术部这实力,老大算了下,写了个Mp3播放,最少得1个月。于是从网站找了找,总算功夫不负有心人,在一个专业网站,找到了Mp3播放的源代码,好评如潮,有2000行,虽然接口与类结构挺清晰,但代码实在看不懂。

  ...
public interface IMusicPlayer { public void PlayMusic(); } public class Mp3MusicPlayer : IMusicPlayer { private string Path { get; set; } public Mp3MusicPlayer(string path) { this.Path = path; } public void PlayMusic() { Console.WriteLine(String.Format("{0} starts playing!", this.Path)); //此去省略2000行,根本看不懂 } }
  ...

  老大范愁了,想重构下,又不想花时间再测试,还想着网上这个作者再升级时,直接拿过来用,还想着,这个类给其它工程用。真是既想马儿好,又想马儿不吃草,还想马儿快快跑!

  4、我灵机一动,说:”做个适配器“。老大看了看我,笑道,我怎么把这茬给忘了呢?好,小孙,这个由你来,我一时无语!

  5、很明显,PlayerListClient的Add要的是IPlayer,而不是IMusicPlayer,我们需要把IMusicPlayer 转成 IPlayer。而这两个明显根本不是一个规格,而是来自于两个独立的系统。好,这就是Adapter的用武之地了。代码如下,这里的Mp3Player就不用写成Mp3PlayerAdapter了,只是个名字而已,而本质上是一个Adapter类,因为它自己并没有实现实质性的生产功能,只是借用别人功能而已。所以同样是IPlayer,Mp3Player与WavPlayer、WmvPlayer是不一样的,前者是Adapter类,而后两者是真正的产品类。

    public class Mp3Player : IPlayer
    {
        private string Music_Path { get; set; }
        public Mp3Player(string music_path) { this.Music_Path = music_path; }
        public void Play()
        {
            Console.WriteLine(String.Format("{0} starts playing!", this.Music_Path));
            //此处不用写3000多行了,2句就够了
            var mp3 = new Mp3MusicPlayer(this.Music_Path);
            mp3.PlayMusic();
        }
    }

  这样即省去3000行代码,也能省去2000行代码的重构成本与测试成本,更重要的是:传统Player的升级与外来的MusicPlayer的两边可以各自升级,因为两者没有任何关联,真做到了马儿三好!

 

posted @ 2018-11-10 14:19  顽强战士  阅读(82)  评论(0)    收藏  举报