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

开发 后端
关于设计模式的说法,网上一搜一大堆,咱就不再去说了。我的理解,设计模式就是很多NB的大佬们总结出来的,用来处理特定情况的标准解决方案。

[[434250]]

关于设计模式的说法,网上一搜一大堆,咱就不再去说了。

我的理解,设计模式就是很多NB的大佬们总结出来的,用来处理特定情况的标准解决方案。

那既然是标准方案,就一定会有套路,会有标准的样子。

几种常见的设计模式,都有各自的套路模板。下面进入主题,咱们一个一个来说。

一、抽象工厂模式

抽象工厂主要的场景是创建一系列相关或相似的对象。注意这个相关或相似。因为有这个特性,我们就可以抽取出共同点,来形成接口或抽象类,然后通过这个接口或抽象类,来派生出各个对象。

为什么叫抽象工厂?不知道,而且不重要。在我看来,这只是个名字,记下就好。一个东西出来,总要有个名字的,有个人跑出来说,就叫抽象工厂吧。于是就叫抽象工厂了。就像一个人,做了一个网站,让别人在上面卖东西,后来做大了,需要吹NB了,得给这个经营方式起个名字,于是,B2C 就出现了。现在人们创业做电商,头一件事先讨论做 B2B 还是 B2C。做反了。先做吧,挣钱要紧。做大了,你说叫什么,都会有人听、有人信。

说跑题了,:P

?

先写几个类型,包括接口和实体类,后面会用到:

  1. // 接口定义 
  2. public interface ILaptop 
  3.     void GetInfo(); 
  4. public interface IMobile 
  5.     void GetInfo(); 
  6.  
  7. // 实体类一 
  8. public class MateBook : ILaptop 
  9.     public void GetInfo() 
  10.     { 
  11.         Console.WriteLine("I am MateBook"); 
  12.     } 
  13. public class Mac : ILaptop 
  14.     public void GetInfo() 
  15.     { 
  16.         Console.WriteLine("I am Mac"); 
  17.     } 
  18.  
  19. // 实体类二 
  20. public class Mate : IMobile 
  21.     public void GetInfo() 
  22.     { 
  23.         Console.WriteLine("I am Mate"); 
  24.     } 
  25. public class IPhone : IMobile 
  26.     public void GetInfo() 
  27.     { 
  28.         Console.WriteLine("I am IPhone"); 
  29.     } 

有了上面的类型,我们来看看抽象工厂的套路。

定义:

  1. public interface IFactory 
  2.     ILaptop CreateLaptop(); 
  3.     IMobile CreateMobile(); 
  4. public class FactoryA : IFactory 
  5.     public ILaptop CreateLaptop() 
  6.     { 
  7.         return new MateBook(); 
  8.     } 
  9.     public IMobile CreateMobile() 
  10.     { 
  11.         return new Mate(); 
  12.     } 
  13. public class FactoryB : IFactory 
  14.     public ILaptop CreateLaptop() 
  15.     { 
  16.         return new Mac(); 
  17.     } 
  18.     public IMobile CreateMobile() 
  19.     { 
  20.         return new IPhone(); 
  21.     } 

调用:

  1. public static class Example 
  2.     public static void ExampleTest() 
  3.     { 
  4.         var factoryA = new FactoryA(); 
  5.         var laptopA = factoryA.CreateLaptop(); 
  6.         var mobileA = factoryA.CreateMobile(); 
  7.         laptopA.GetName(); 
  8.         mobileA.GetName(); 
  9.  
  10.         var factoryB = new FactoryB(); 
  11.         var laptopB = factoryB.CreateLaptop(); 
  12.         var mobileB = factoryB.CreateMobile(); 
  13.         laptopB.GetName(); 
  14.         mobileB.GetName(); 
  15.     } 
  16.     //result : 
  17.     //I am MateBook 
  18.     //I am Mate 
  19.     //I am Mac 
  20.     //I am IPhone 

这个模式里面,核心的部分就是工厂接口的定义:

  1. public interface IFactory 
  2.     ILaptop CreateLaptop(); 
  3.     IMobile CreateMobile(); 

在这个工厂接口中,加入了多个相似的接口。于是,调用端可以很简单的以类似的方式去调用,而工厂实体中,对内部引用的实体进行区分。一个典型的场景是:一个程序,对着很多种数据库。这样的情况,可以在工厂实体中区分具体连接哪种数据库,而内部的引用实体,则各自对应不同的数据库。外部调用时,只需要在初始化时确认使用哪种数据库,后面的 CRUD 操作,就直接使用就成,调用端不需要考虑数据库的区别。事实上,这也是抽象工厂用的最多的场景。

二、工厂模式

去掉了抽象两个字,居然还是一个模式,而且是一个不同的模式。这个名字起的够混乱。

不过老实说,工厂模式跟抽象工厂模式很像,区别是:抽象工厂模式是在工厂模式的基础上,又做了一层抽象。

看套路:

  1. public interface IFactory 
  2.     ILaptop CreateLaptop(); 
  3. public class FactoryA : IFactory 
  4.     public ILaptop CreateLaptop() 
  5.     { 
  6.         return new MateBook(); 
  7.     } 
  8. public class FactoryB : IFactory 
  9.     public ILaptop CreateLaptop() 
  10.     { 
  11.         return new Mac(); 
  12.     } 
  13. public static class Example 
  14.     public static void Test() 
  15.     { 
  16.         var factoryA = new FactoryA(); 
  17.         var laptopA = factoryA.CreateLaptop(); 
  18.         laptopA.GetName(); 
  19.  
  20.         var factoryB = new FactoryA(); 
  21.         var laptopB = factoryB.CreateLaptop(); 
  22.         laptopB.GetName(); 
  23.     } 
  24.     //result : 
  25.     //I am MateBook 
  26.     //I am Mac 

看到了吧,跟抽象工厂确实很相似。不过在使用上,工厂模式用得会更多。任何类都可以按工厂模式来写。这个模式最大的作用,是把定义和实体做了分层。开发时,可以一部分人去定义接口,而另一部分人去实现这个接口。而且,工作模式可以随时扩展为抽象工厂。比方一开始只是可能有多种数据库,但具体是哪些数据库还没确定,就可以先按工厂模式写,等数据库定下来,随时就很容易转为抽象工厂了。

三、建造者模式

这个名称起的更不知所云了,就因为一个 Builder?

其实他说的是这么个事。我们经常看到这样的代码:

  1. var mobile = new MobileBuilder() 
  2.                 .WithBrand("Apple"
  3.                 .WithModel("13Pro"
  4.                 .WithMemory("512G"
  5.                 .Build(); 

看着是不是很洋气?

来看看这个套路:

  1. // 这就是个数据模型 
  2. public class Mobile 
  3.     public string Brand { get; set; } 
  4.     public string Model { get; set; } 
  5.     public string Memory { get; set; } 
  6.  
  7. // 这才是 Builder 的定义 
  8. public class MobileBuilder 
  9.     private readonly Mobile _mobile = new Mobile(); 
  10.     public MobileBuilder WithBrand(string brand) 
  11.     { 
  12.         _mobile.Brand = brand; 
  13.         return this; 
  14.     } 
  15.     public MobileBuilder WithModel(string model) 
  16.     { 
  17.         _mobile.Model = model; 
  18.         return this; 
  19.     } 
  20.     public MobileBuilder WithMemory(string memory) 
  21.     { 
  22.         _mobile.Memory = memory; 
  23.         return this; 
  24.     } 
  25.     public Mobile Build() 
  26.     { 
  27.         return _mobile; 
  28.     } 

然后就可以这样调用了:

  1. public static class Example 
  2.     public static void Test() 
  3.     { 
  4.         var mobile = new MobileBuilder() 
  5.                         .WithBrand("Apple"
  6.                         .WithModel("13Pro"
  7.                         .WithMemory("512G"
  8.                         .Build(); 
  9.         Console.WriteLine(mobile.ToJson()); 
  10.     } 
  11.     //result : 
  12.     //{"Brand":"Apple","Model":"13Pro","Memory":"512G"

个人而言,我很喜欢这个套路,没有别的,就是洋气,非常的洋气。应用场景也非常多,所有数据的 DTO,都可以么写。

四、原型模式

这个模式听着会有点陌生。看过一些文章,也把它归为是创建型模式。实际上,我更倾向于把它看作是一种代码结构,而不是模式。这种结构最大的作用,是复制 - 通过复制一个存在的实例来创建新实例。

代码是这样的:

  1. public class MobilePackage 
  2.     public string Color { get; set; } 
  3.     public Mobile Mobile { get; set; } 
  4.  
  5.       // 下面才是模式代码 
  6.     public MobilePackage ShallowCopy() 
  7.     { 
  8.         return (MobilePackage)this.MemberwiseClone(); 
  9.     } 
  10.     public MobilePackage DeepCopy() 
  11.     { 
  12.         var clone = (MobilePackage)this.MemberwiseClone(); 
  13.         clone.Color = new string(Color); 
  14.         clone.Mobile = new Mobile 
  15.         { 
  16.             Brand = new string(Mobile.Brand), 
  17.             Model = new string(Mobile.Model), 
  18.             Memory = new string(Mobile.Memory), 
  19.         }; 
  20.         return clone; 
  21.     } 

看看,其实就是一段复制代码。

但是要注意,对于深拷贝和浅拷贝,涉及到指针和引用,如果你不熟悉,了解后再用。看一下上面的结果:

  1. public static class Example 
  2.     public static void Test() 
  3.     { 
  4.         var mobilePackage = new MobilePackage 
  5.         { 
  6.             Color = "White"
  7.             Mobile = new Mobile 
  8.             { 
  9.                     Brand = "Apple"
  10.                     Model = "13Pro"
  11.                     Memory = "512G"
  12.             } 
  13.         }; 
  14.  
  15.         var shallowCopied = mobilePackage.ShallowCopy(); 
  16.         var deepCopied = mobilePackage.DeepCopy(); 
  17.  
  18.         mobilePackage.Color = "Black"
  19.         mobilePackage.Mobile.Brand = "Huawei"
  20.         mobilePackage.Mobile.Model = "Mate"
  21.  
  22.         Console.WriteLine(mobilePackage.ToJson()); 
  23.         Console.WriteLine(shallowCopied.ToJson()); 
  24.         Console.WriteLine(deepCopied.ToJson()); 
  25.     } 
  26.  
  27.     //result: 
  28.     //{"Color":"Black","Mobile":{"Brand":"Huawei","Model":"Mate","Memory":"512G"}} 
  29.     //{"Color":"White","Mobile":{"Brand":"Huawei","Model":"Mate","Memory":"512G"}} 
  30.     //{"Color":"White","Mobile":{"Brand":"Apple","Model":"13Pro","Memory":"512G"}} 

结果和你理解的是不是一样?如果不一样,去研究一下值和引用的区别。另外,C# 10 里新出来的 Record,就是一个典型的原型模式的类型,也可以了解一下。

五、单例模式

单例模式也是一个用处非常大的模式,而且这个名字起得挺直白。

单例模式,简单点说就是不管你 new 多少回,实际应用全局内存中只会有一份实例。

套路代码特别简单:

  1. public sealed class Singleton 
  2.     private static Singleton _instance; 
  3.     private static readonly object _locker = new object(); 
  4.  
  5.     private Singleton() 
  6.     { 
  7.     } 
  8.  
  9.     public static Singleton GetInstance() 
  10.     { 
  11.         if (_instance == null
  12.         { 
  13.             lock (_locker) 
  14.             { 
  15.                 if (_instance == null
  16.                 { 
  17.                     _instance = new Singleton(); 
  18.                 } 
  19.             } 
  20.         } 
  21.         return _instance; 
  22.     } 

这里有两个注意点:

类声明用到 sealed 关键字,以确保这个类不会被派生。

类构造函数用了 private,以确保这个类不会被 new。这本身与单例无关,只是通过这种方式来表明这是一个单例。控制单例的最核心的代码,其实是下面的 GetInstance() 方法。

调用时,就是下面一行代码:

  1. Singleton singleton = Singleton.GetInstance(); 

就OK了。

设计模式有很多种,对应的套路也有很多。其中,有一些是简单无脑的套路,像上面的单例,而另一些就会比较复杂。

不过,既然是套路,总是有固定的代码或结构可循的。

我这个主题,打算分几篇来写。这是第一篇。

 

最后做个小注解:套路虽简单,也要吃透了再用。而且,有时候简单的代码就能很好地完成任务,一定不要过度使用。

 

责任编辑:武晓燕 来源: 老王Plus
相关推荐

2021-12-01 09:23:13

C# 模式套路

2024-03-20 10:59:37

开源

2009-08-26 10:24:04

C# Observer

2009-09-02 16:23:27

C# Singleto

2009-04-29 09:06:18

C#设计模式Adapter

2009-08-31 16:01:31

C#和设计模式

2013-02-26 10:55:47

C#适配器设计模式

2023-01-13 09:53:32

2009-08-04 09:22:26

C#工厂模式

2010-11-29 09:26:05

jQuery特效

2009-08-31 16:12:02

C#使用Singlet

2019-04-22 15:00:05

CSS前端开发

2009-09-07 04:59:12

C#模式窗体

2009-08-26 16:26:37

C#迭代器模式

2009-08-11 13:59:41

迭代器模式C# Iterator

2017-01-03 17:51:21

AndroidViewHolder工具类

2013-07-22 10:01:03

JavascriptWeb

2009-08-24 16:11:35

C#项目开发

2009-09-07 09:53:01

C# DisposeDispose方法

2009-08-25 18:04:30

C#实现Singlet
点赞
收藏

51CTO技术栈公众号