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

设计模式知道,套路是个啥?

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

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

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

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

一、抽象工厂模式

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

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

说跑题了,:P

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

// 接口定义
public interface ILaptop
{void GetInfo();
}
public interface IMobile
{void GetInfo();
}// 实体类一
public class MateBook : ILaptop
{public void GetInfo(){Console.WriteLine("I am MateBook");}
}
public class Mac : ILaptop
{public void GetInfo(){Console.WriteLine("I am Mac");}
}// 实体类二
public class Mate : IMobile
{public void GetInfo(){Console.WriteLine("I am Mate");}
}
public class IPhone : IMobile
{public void GetInfo(){Console.WriteLine("I am IPhone");}
}

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

定义:

public interface IFactory
{ILaptop CreateLaptop();IMobile CreateMobile();
}
public class FactoryA : IFactory
{public ILaptop CreateLaptop(){return new MateBook();}public IMobile CreateMobile(){return new Mate();}
}
public class FactoryB : IFactory
{public ILaptop CreateLaptop(){return new Mac();}public IMobile CreateMobile(){return new IPhone();}
}

调用:

public static class Example
{public static void ExampleTest(){var factoryA = new FactoryA();var laptopA = factoryA.CreateLaptop();var mobileA = factoryA.CreateMobile();laptopA.GetName();mobileA.GetName();var factoryB = new FactoryB();var laptopB = factoryB.CreateLaptop();var mobileB = factoryB.CreateMobile();laptopB.GetName();mobileB.GetName();}//result ://I am MateBook//I am Mate//I am Mac//I am IPhone
}

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

public interface IFactory
{ILaptop CreateLaptop();IMobile CreateMobile();
}

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

二、工厂模式

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

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

看套路:

public interface IFactory
{ILaptop CreateLaptop();
}
public class FactoryA : IFactory
{public ILaptop CreateLaptop(){return new MateBook();}
}
public class FactoryB : IFactory
{public ILaptop CreateLaptop(){return new Mac();}
}
public static class Example
{public static void Test(){var factoryA = new FactoryA();var laptopA = factoryA.CreateLaptop();laptopA.GetName();var factoryB = new FactoryA();var laptopB = factoryB.CreateLaptop();laptopB.GetName();}//result ://I am MateBook//I am Mac
}

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

三、建造者模式

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

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

var mobile = new MobileBuilder().WithBrand("Apple").WithModel("13Pro").WithMemory("512G").Build();

看着是不是很洋气?

来看看这个套路:

// 这就是个数据模型
public class Mobile
{public string Brand { get; set; }public string Model { get; set; }public string Memory { get; set; }
}// 这才是 Builder 的定义
public class MobileBuilder
{private readonly Mobile _mobile = new Mobile();public MobileBuilder WithBrand(string brand){_mobile.Brand = brand;return this;}public MobileBuilder WithModel(string model){_mobile.Model = model;return this;}public MobileBuilder WithMemory(string memory){_mobile.Memory = memory;return this;}public Mobile Build(){return _mobile;}
}

然后就可以这样调用了:

public static class Example
{public static void Test(){var mobile = new MobileBuilder().WithBrand("Apple").WithModel("13Pro").WithMemory("512G").Build();Console.WriteLine(mobile.ToJson());}//result ://{"Brand":"Apple","Model":"13Pro","Memory":"512G"}
}

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

四、原型模式

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

代码是这样的:

public class MobilePackage
{public string Color { get; set; }public Mobile Mobile { get; set; }// 下面才是模式代码public MobilePackage ShallowCopy(){return (MobilePackage)this.MemberwiseClone();}public MobilePackage DeepCopy(){var clone = (MobilePackage)this.MemberwiseClone();clone.Color = new string(Color);clone.Mobile = new Mobile{Brand = new string(Mobile.Brand),Model = new string(Mobile.Model),Memory = new string(Mobile.Memory),};return clone;}
}

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

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

public static class Example
{public static void Test(){var mobilePackage = new MobilePackage{Color = "White",Mobile = new Mobile{Brand = "Apple",Model = "13Pro",Memory = "512G",}};var shallowCopied = mobilePackage.ShallowCopy();var deepCopied = mobilePackage.DeepCopy();mobilePackage.Color = "Black";mobilePackage.Mobile.Brand = "Huawei";mobilePackage.Mobile.Model = "Mate";Console.WriteLine(mobilePackage.ToJson());Console.WriteLine(shallowCopied.ToJson());Console.WriteLine(deepCopied.ToJson());}//result://{"Color":"Black","Mobile":{"Brand":"Huawei","Model":"Mate","Memory":"512G"}}//{"Color":"White","Mobile":{"Brand":"Huawei","Model":"Mate","Memory":"512G"}}//{"Color":"White","Mobile":{"Brand":"Apple","Model":"13Pro","Memory":"512G"}}
}

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

五、单例模式

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

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

套路代码特别简单:

public sealed class Singleton
{private static Singleton _instance;private static readonly object _locker = new object();private Singleton(){}public static Singleton GetInstance(){if (_instance == null){lock (_locker){if (_instance == null){_instance = new Singleton();}}}return _instance;}
}

这里有两个注意点:

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

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

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

Singleton singleton = Singleton.GetInstance();

就OK了。

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

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

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/295735.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何让你的碎片化时间更有价值?

全世界只有3.14 % 的人关注了爆炸吧知识不知道大家有没有发现,我们开始变得浮躁起来了,我们不再愿意花大量时间去做成一件事情,也无法下定决心,改变自己糟糕的现状。却羡慕着手机屏幕里各种各样的成功人士,活出了自己最…

窥探Swift编程之强大的Switch

之前初识Swift中的Switch语句时,真的是让人眼前一亮,Swift中Switch语句有好多特有而且特好用的功能。说到Switch, 只要是写过程序的小伙伴对Switch并不陌生。其在程序中的出镜率还是比较高档。Switch属于程序的分支语句,Switch的功能便于处理…

安富莱v6开发板网口通讯_安富莱嵌入式周报第170期:2020.07.202020.07.26

说明:谢谢大家的关注,继续为大家盘点上周精彩内容。1、沁恒推出带USB3.0,千兆以太网,光纤接口的RISC内核单片机CH569 CH569/565 微控制器使用 RISC-V3A 内核, 支持 RISC-V 指令的 IMAC 子集。片上集成超高速USB3.0主…

postfix搭建及配置

一. 电子邮件系统概述1. 邮件系统角色MUA(邮件用户代理)、MTA(邮件传输代理)、MDA(邮件分发代理)2. 邮件应用协议SMTP,简单邮件传输协议,TCP 25端口POP3&…

连续子数组的最大和

有一个整数数组,求出连续子数组的和的最大值。有一个首尾相连的整数数组,求出连续子数组的和的最大值。在数组中,数字减去它右边的数字得到一个数对之差。求所有数对之差的最大值。1、思路: 动态规划思路:用函数f(i)表…

Android之常用开发框架

Android之常用开发框架 1、Rajawali 介绍: 安卓的OpenGL ES 2.0/3.0 引擎。可以用于制作普通应用或者动态壁纸,当然也可以用于制作游戏。 项目地址: https://github.com/Rajawali/Rajawali 2、RxAndroid 介绍: RxAndroid是RxJava的一个针对Android平台的扩展。它包含了一些…

servlet需要和ajax,如何使用Servlet和Ajax?

我是Web应用程序和Servlet的新手,我有以下问题:每当我在Servlet中打印某些内容并由网络浏览器调用它时,它将返回一个包含该文本的新页面。 有没有一种方法可以使用Ajax在当前页面中打印文本?#1楼我将向您展示servlet的整个示例以及…

如何阻止给 一个程序 开启多个实例 ?

咨询区 C. Dragon 76:在 .NET 中是否有比较好的方法可以阻止一个 application 被同时开启了多个实例?如果没有好的办法,那么只能退其次,给每个 application 配一些操作规范。回答区 ImJustPondering:我总结有两种解法。…

WPF中设置了WindowStyle=None后,窗口仍然有边框的解决方法

1. 设置了窗体的WindowStyle"None",窗口还是右边框,如下图: 2. 这是因为窗体默认是可以改变大小的,所以需要修改ResizeMode的值 ResizeMode"NoResize", 这样设置后,上图中的边框就没有了

两度破译“白宫密码”,让美国政府部门崩溃,却称自己是抱娃敲代码的普通妈妈

全世界只有3.14 % 的人关注了爆炸吧知识抱娃敲代码称成功是幸运而已今天我们来点正经的,来跟大家分享一位乘风破浪的姐姐--王小云教授。自踏入2020年以来,山东大学网络空间安全学院(研究院)院长,双聘院士、讲席教授王小…

github java开源项目经验_10月份Github上最热门的Java开源项目

10 月份 GitHub 上最热门的Java开源项目排行已经出炉啦,在本月的名单中,实战项目类居多,当然也有像JavaGuide这样学习指南类项目,下面就是本月上榜的10个开源项目:1、Java(Star 18468)https://g…

转载:保护模式1

实模式与保护模式 摘自ITWIKI,为我和还没搞明白真实模式保护模式的XDJM们 真实模式 Intel 8086和8088有14个16位缓存器。其中四个(AX, BX, CX, DX)是通用目的(尽管每个缓存器有额外的目的;举个例子:CX可以被用来当作lo…

浙江移动无线dns服务器地址,浙江移动4g的dns服务器地址

浙江移动4g的dns服务器地址 内容精选换一换当创建文件系统后,您需要使用云服务器来挂载该文件系统,以实现多个云服务器共享使用文件系统的目的。本章节以Windows 2012版本操作系统为例进行NFS文件系统挂载,其他版本请参考以下主要步骤根据实际…

Android之给gridview的单元格加上分割线

有时候需要给gridview加上分割线,没有现成的解决方案,这里写好一个可以直接用的自定义gridview,就叫做LineGridView吧。先上图,zaker客户端第三方分享的gridview样式: 可以看到靠边的格子都是半封闭的,要实…

Azure Machine Learning - 如何使用 GPT-4 Turbo with Vision

介绍如何在Azure中使用GPT-4 Turbo with Vision 关注TechLead,分享AI全维度知识。作者拥有10年互联网服务架构、AI产品研发经验、团队管理经验,同济本复旦硕,复旦机器人智能实验室成员,阿里云认证的资深架构师,项目管理…

系统命名与 SQL 命名之争 - 第 1 部分

创建数据库对象的命名规范 创建数据库对象时,开发人员可以选择一种命名方法,可以选择遵循传统 IBM i 行为的系统命名模式 (*SYS),也可以选择遵循SQL 标准规则的 SQL 命名规范 (*SQL)。 DB2 for i 与其他数据库管理系统 (DBMS) 之间的主要差别…

C++ const 关键字使用

2019独角兽企业重金招聘Python工程师标准>>> 为什么使用const?采用符号常量写出的代码更容易维护;指针常常是边读边移动,而不是边写边移动;许多函数参数是只读不写的。const最常见用途是作为数组的界和switch分情况标号…

论大象如何装进冰箱

全世界只有3.14 % 的人关注了爆炸吧知识在这美好的一天,想和大家讨论一个有趣的数学话题:怎么才能将一只大象装进冰箱呢?数学的方法把大象放到冰箱里的分析学方法1)先把大象微分,然后把它放到冰箱里,再在冰…

2020下半年新机最新消息_提前剧透 2020 年下半年五大新机

华为 Mate40系列 美帝一系列封杀骚操作,并没有影响到华为手机下一代的处理器——台积电代工的、采用了 5nm 进程的 Mate40 处理器——麒麟 1020。为了 3D 脸部识别,可能还是采用刘海屏。Mate40 Pro 的曲率更大,正面屏幕的视觉冲击更加震撼&am…

手把手教你学Dapr - 5. 状态管理

介绍使用状态管理,您的应用程序可以将数据作为键/值对存储在支持的状态存储中。您的应用程序可以使用 Dapr 的状态管理 API 使用状态存储组件来保存和读取键/值对,如下图所示。例如,通过使用 HTTP POST,您可以保存键/值对&#xf…