北川广海の梦

北川广海の梦

设计模式:适配器与装饰器

2021-11-01

装饰器模式

我们常常面临一个问题,在原有的功能上进行扩展,如何优雅的进行实现?
有人会说,给这个类加一个新方法不就得了。这不就违背了开闭原则吗?类应该对扩展开放,对修改封闭。

一般常见有两种方式,一是直接继承于想要扩展的类,那么我们就能够在原有功能的基础上,扩展更多的方法。下面将进行简单实现:

  class Phone
    {
        void Call()
        {
            
        }

        void SendMessage()
        {
            
        }
    }

    class MusicPhone : Phone
    {
        void PlayMusic()
        {
            
        }
    }

    class GamePhone : Phone
    {
        void PlayGame()
        {
            
        }
    }

我们申明了一个手机类,它初始只有两个功能,打电话和发短信。
然后我们通过继承,给这个手机增加了播放音乐的功能。同时,我们还造了一部能玩游戏的手机。
缺点
装饰器模式能够很好的帮助我们对一个类进行扩展,并且符合开闭原则。但是随着功能越来越多,静态的通过继承来进行实现,会造成代码量迅速膨胀

同时通过以下方法也是可行的,并且可以有效解决继承带来的弊端。:

     interface IPhone
    {
        void Call();

        void SendMessage();
    }

    class Phone : IPhone
    {
        public void Call()
        {
        }

        public void SendMessage()
        {
        }
    }

    class MediaPhone : IPhone
    {
        private readonly IPhone _phone;

        public MediaPhone(IPhone phone)
        {
            _phone = phone;
        }

        void PlayMusic()
        {
        }

        void PlayGame()
        {
        }

        public void Call()
        {
            _phone.Call();
        }

        public void SendMessage()
        {
            _phone.SendMessage();
        }
    }

通过关联某一个类,然后在此基础上进行扩展。

C#还有一个装饰模式利器,那就是静态扩展方法。同样还是手机的例子:

class Program
    {
        static void Main(string[] args)
        {
            var phone = new Phone();
            phone.PlayMusic();
        }
    }

    class Phone
    {
        void Call()
        {
        }

        void SendMessage()
        {
        }
    }

    static class PhoneExtension
    {
        public static void PlayMusic(this Phone phone)
        {
        }
    }

通过一个静态类,实现一个静态方法,就可以达到扩展的目的,这种方式被官方类库广泛使用,最常见的就是Linq程序集下对IEnumable接口的扩展了。

适配器模式

如果装饰器模式是为了扩展,那么适配器模式就是为了兼容。
虽然我们普遍存在接口,只要我们依赖同样的接口,就能无需关心实现。但是如果接口层次不兼容如何解决呢?修改接口的话,会对代码进行侵入性的更改。
通过适配器模式,可以较好的解决这个问题。

还是手机的例子:

 class Program
    {
        static void Main(string[] args)
        {
            var phone = new Phone();
            IWatch adapter = new WatchAdapter(phone);
            TellMeTime(adapter);
        }

        static void TellMeTime(IWatch watch)
        {
            watch.ShowTime();
        }
    }

    interface IPhone
    {
        void Call();

        void SendMessage();

        void ShowTime();
    }

    class Phone : IPhone
    {
        public void Call()
        {
        }

        public void SendMessage()
        {
        }

        public void ShowTime()
        {
        }
    }

    interface IWatch
    {
        void ShowTime();
    }

    class WatchAdapter : IWatch
    {
        private readonly IPhone _phone;

        public WatchAdapter(IPhone phone)
        {
            _phone = phone;
        }

        public void ShowTime() => _phone.ShowTime();
    }

我们的程序需要一个手表来显示时间,但是现在没有手表,找来找去,发现只有手机可以用,并且用它来显示时间也是合适的。可是TellMeTime方法接收的参数是IWatch类型,手机并没有实现这个接口。此时我们就可以使用一个Adapter来封装一下手机,并且实现IWatch接口,即可满足我们的需求。

总结

装饰器模式主要应用于扩展,扩展丰富了类的功能,但却并不是类的一部分。他们之间是没有必然关系的。
适配器是为了解决两个不兼容的类或接口,使他们能够正常的进行功能配合而生。