值得永久收藏的 C# 设计模式套路(二)

共 8362字,需浏览 17分钟

 ·

2022-01-02 12:03

设计模式套路,第二弹。

在第一篇中,主要写了创造模式相关的几种套路。如果你是刚看到这个文章,建议你先去看看第一篇 传送门

这一篇,我们说说结构模式相关的套路。

结构模式,主要作用是将类型、对象和其它内容放在一起,以创建更大的结构,同时,又可以保持高度的灵活性和最佳性能。

也是像上一篇一样,一个一个来说。

一、适配器模式

适配器这个名字非常好理解,就像我们充电器的插头,是用来协调两个不同的东东之间的通信,并让他们互相理解。

代码也很简单:

public class AnotherType
{

    public string GetAuthorInfo()
    
{
        return "I am WangPlus";
    }
}
public interface IAdapter
{
    string GetInfo();
}
public class Adapter : IAdapter
{
    private readonly AnotherType _anotherType;
    public Adapter(AnotherType anotherType)
    
{
        _anotherType = anotherType;
    }
    public string GetInfo()
    
{
        return _anotherType.GetAuthorInfo();
    }
}
public class Example
{

    public void Test()
    
{
        var adapter = new Adapter(new AnotherType());
        Console.WriteLine(adapter.GetInfo());
    }
    // result:
    // I am WangPlus
}

没想到吧?这样的代码经常写,居然也是个模式。所以呀,还是我上一篇提到的说法:先有内容,然后才有概念和标准。套路一直在,只是很多人不知道他叫什么。

二、桥模式

这个也好理解,就是在两个东西之间搭了一个桥。

正常使用时,是把实体类与接口和抽象分离开。有一个非常明显的好处,是几个实现可以使用不同的技术。

理解概念有点难,还是看代码:

public interface IBridgeType
{
    void GetInfo();
}
public class BridgeA : IBridgeType
{
    public void GetInfo()
    
{
        Console.WriteLine("I am WangPlus");
    }
}
public class BridgeB : IBridgeType
{
    public void GetInfo()
    
{
        Console.WriteLine("I am another WangPlus");
    }
}
public interface IBridge
{
    public IBridgeType bridgeType
    {
        get;
        set;
    }
    void GetInfo();
}
public class BridgeType : IBridge
{
    public IBridgeType bridgeType
    {
        get;
        set;
    }
    public void GetInfo()
    
{
        bridgeType.GetInfo();
    }
}
public static class BridgeExample
{

    public static void Test()
    
{
        var bridgeType = new BridgeType();
        bridgeType.bridgeType = new BridgeA();
        bridgeType.GetInfo();
        bridgeType.bridgeType = new BridgeB();
        bridgeType.GetInfo();
    }
    // result:
    // I am WangPlus
    // I am another WangPlus
}

BridgeA 和 BridgeA 是两个实现,这儿就是上面说的不同的技术,用不同的技术实现了同一个接口。然后通过 IBridge 桥接到一个实现中。

使用时,使用不同的实现,但用相同的结构进行调用。在有需要时,我们可以根据场景做出无数个 BridgeN ,来实现黑盒类似但白盒完全不同的实体。

三、复合模式

听着就大就复杂。没错,所有叫复合的东西,都会形成一个树状结构。这好像是编程中的一个默认约定?

复合设计模式,就是把对象放在一个更大的树状结构的对象中,以多层次结构来呈现对象,以统一方式处理对象。

看看这个复杂代码的套路:

public abstract class Mobile
{

    protected string Name;
    protected Mobile(string name)
    
{
        Name = name;
    }
    public virtual void Add(Mobile mobile)
    
{
        throw new NotImplementedException();
    }
    public virtual void GetTree(int indent)
    
{
        throw new NotImplementedException();
    }
}
public class MobileMemory : Mobile
{
    public MobileMemory(string name) : base(name) { }
    public override void GetTree(int indent)
    
{
        Console.WriteLine(new String('-', indent) + " " + Name);
    }
}
public class MobileModel : Mobile
{
    private readonly List _mobiles = new List();
    public MobileModel(string name) : base(name) { }
    public override void Add(Mobile mobile)
    
{
        _mobiles.Add(mobile);
    }
    public override void GetTree(int indent)
    
{
        Console.WriteLine(new String('-', indent) + "+ " + Name);

        foreach (var mobile in _mobiles)
        {
            mobile.GetTree(indent + 2);
        }
    }
}
public static class Example
{

    public static void Test()
    
{
        var brand = new MobileModel("IPhone");

        var model13 = new MobileModel("13Pro");
        var model12 = new MobileModel("12Pro");

        var memory512G = new MobileMemory("512G");
        var memory256G = new MobileMemory("256G");

        model13.Add(memory256G);
        model13.Add(memory512G);

        model12.Add(memory256G);
        model12.Add(memory512G);

        brand.Add(model12);
        brand.Add(model13);

        brand.GetTree(1);
    }
    // result:
    // ---+ 12Pro
    // ----- 256G
    // ----- 512G
    // ---+ 13Pro
    // ----- 256G
    // ----- 512G
}

这个套路确实稍微有点复杂。补充解释一下:

  • MobileMemory 和 MobileModel,是为了表现多种对象,没有特殊含义,里面的区别就是 GetTree() 里打印出来的字符不同。

  • 需要清楚理解的部分是 MobileModel 里构建的 _mobiles,他是一个顶层抽象类的数组。

  • 这个模式最重要的结构,是用抽象类去组织数据,用实体类去操作功能。

另外,如果你的开发功力够,在这个架构中,实体本身也可以是复合对象。

四、装饰模式

这也是一个常用的模式。通过对抽象或接口的扩展,来加入对象功能。

而且这个套路的代码特别好理解:

public interface IMobile
{
    public string Brand
    {
        get;
    }
    public string Model
    {
        get;
    }
    public abstract void GetInfo();
}
public class IPhone : IMobile
{
    public string Brand => "Apple";
    public string Model => "13Pro";
    public void GetInfo()
    
{
        Console.WriteLine(this.ToJson());
    }
}
public class IPhoneWithMemory : IMobile
{
    private readonly IMobile _mobile;
    public IPhoneWithMemory(IMobile mobile)
    
{
        _mobile = mobile;
    }

    public string Brand => "Apple";
    public string Model => "13Pro";
    public string Memory => "512G";
    public void GetInfo()
    
{
        Console.WriteLine(this.ToJson());
    }
}
public static class Example
{

    public static void Test()
    
{
        var iphone = new IPhone();
        iphone.GetInfo();
        var iphoneWithMemory = new IPhoneWithMemory(iphone);
        iphoneWithMemory.GetInfo();
    }
    // result:
    // {"Brand":"Apple","Model":"13Pro"}
    // {"Brand":"Apple","Model":"13Pro","Memory":"512G"}
}

从上边的 IMobile 接口开始,每一个实体都是对前一个实体的补充和完善。

这种写法,在团队项目中很常见,可以在确保不对别人的内容进行修改的基础上,扩展新的功能。不用改别人的代码,又能补充进去新的内容。有没有被爽到?

五、外观模式

这个模式名称起得不知所云。不过意思和代码倒是很简单,就是把其它的接口、类、框架等的复杂系统汇集起来,让人能简单使用。

代码一看就懂:

public class Facade
{

    private readonly Mobile _mobile;
    private readonly Laptop _laptop;
    public Facade(Mobile mobile, Laptop laptop)
    
{
        _mobile = mobile;
        _laptop = laptop;
    }
    public void GetInfo()
    
{
        _mobile.GetInfo();
        _laptop.GetInfo();
    }
}
public class Mobile
{

    public void GetInfo()
    
{
        Console.WriteLine("I am mobile");
    }
}
public class Laptop
{

    public void GetInfo()
    
{
        Console.WriteLine("I am laptop");
    }
}
public static class Example
{

    public static void Test()
    
{
        var mobile = new Mobile();
        var laptop = new Laptop();
        var facade = new Facade(mobile, laptop);
        facade.GetInfo();
    }
    // result:
    // I am mobile
    // I am laptop
}

这个模式,在开发中也用得比较多。尤其在团队项目中,会经常用到,原因跟上面一样。

六、轻量级模式

嗯,就是轻的意思。这个轻,不是写的少,而是内存使用少。

所以这个模式的主要优势,就是节省内存。

这个模式没办法给出简单的套路。他本身是一种想法,是一种写在代码中的思想,而不是一个套路性的代码组。

我拿一段代码来说明一下:

public class Flyweight
{

    private readonly Liststring, DemoClass>> _sharedObjects = new();
    public Flyweight()
    
{
        _sharedObjects.Add(new KeyValuePair<string, DemoClass>("A"new DemoClass()));
        _sharedObjects.Add(new KeyValuePair<string, DemoClass>("B"new DemoClass()));
    }
    public DemoClass GetObject(string key)
    
{
        return _sharedObjects.SingleOrDefault(c => c.Key == key).Value;
    }
}
public interface IDemoClass
{
    public void Operation(string name);
}
public class DemoClass : IDemoClass
{
    public void Operation(string name)
    
{
        Console.WriteLine(name);
    }
}
public static class Example
{

    public static void Test()
    
{
        var flyweight = new Flyweight();
        flyweight.GetObject("A").Operation("Hello");
        flyweight.GetObject("B").Operation("I am WangPlus");

        var heavy = new DemoClass();
        heavy.Operation("Hello, I am WangPlus");
    }
    // result:
    // 下面是轻量级模式
    // Hello
    // I am WangPlus
    // 下面是普通模式
    // Hello, I am WangPlus
}

在这段代码里,真正属于轻量级模式模式的其实只是里面的这一段:

    private readonly Liststring, DemoClass>> _sharedObjects = new();
    public Flyweight()
    
{
        _sharedObjects.Add(new KeyValuePair<string, DemoClass>("A"new DemoClass()));
        _sharedObjects.Add(new KeyValuePair<string, DemoClass>("B"new DemoClass()));
    }

能理解吗?这一段主要是构造了一个集合,用来存放对象。后面调用对象时,是从这个集合里出来的。

这样写的好处,是如果对象很多,每次 new 会占用大量内存,而先期存储在一个集合中,会让这个内存占用变得小很多。

好吧,如果不理解,也没关系。在 Dotnet 的整个源码中,这样使用的也并不多。

所以这个模式属于一个可以意会的模式。而且事实上,现在的内存成本之低,已经很少需要这么费心了。

七、代理模式

这个模式也好理解,就是加了一个代理。通过中间类型来控制对于主类型的访问。

嗯,别担心,这个是有套路的。

public abstract class MainAbst
{

    public abstract void GetInfo();
}
public class MainClass : MainAbst
{
    public override void GetInfo()
    
{
        Console.WriteLine("I am WangPlus");
    }
}
public class Proxy : MainAbst
{
    private MainClass _main;
    public Proxy(MainClass main)
    
{
        _main = main;
    }
    public override void GetInfo()
    
{
        _main ?? = new MainClass();
        _main.GetInfo();
    }
}
public static class ProxyExample
{

    public static void Test()
    
{
        var proxy = new Proxy(new MainClass());
        proxy.GetInfo();
    }
    // result:
    // I am WangPlus
}

这个套路也容易懂。MainClass 是我们的主类,在执行一些特定的方法。加出了一个代理类 Proxy。外部调用时,通过 Proxy 来调用主类的方法,同时,如果有需要对主类的输入输出进行处理,可以在 Proxy 的方法里直接写。

又是一个团队协作会用到的模式,嘿嘿。

结构模式的套路就是这样了。

还有一类模式,是行为设计模式。咱们改天再写。

喜欢就来个三连,让更多人因你而受益


浏览 16
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报