一、剩余设计模式
介绍设计模式,尽量使用我们常见的系统类库来举例,方便带入理解
1.1 桥接模式
定义:类存在多维度独立变化,应采用组合方式,而非继承方式,防止子类爆炸
何为类爆炸:当一个Shape存在形状、颜色变化时,如果只用子类继承方式实现,则会导致实现类过多
public abstract class Shape { public virtual void Print() { Console.WriteLine("I'm Shape"); } }
public class Circle : Shape { public override void Print() { Console.WriteLine("I'm Circle"); } }
public class Square : Shape { public override void Print() { Console.WriteLine("I'm Square"); } }
public class RedCircle : Circle
{
public override void Print()
{
Console.WriteLine("I'm Red Circle");
}
}
public class BlueCircle : Circle
{
public override void Print()
{
Console.WriteLine("I'm Blue Circle");
}
}
public class RedSquare : Square
{
public override void Print()
{
Console.WriteLine("I'm Red Square");
}
}
public class BlueSquare : Square
{
public override void Print()
{
Console.WriteLine("I'm Blue Square");
}
}
桥接模式:
public interface IColor { string GetColor(); }
public class RedColor : IColor { public string GetColor() { return "Red"; } }
public class BlueColor : IColor { public string GetColor() { return "Red"; } }
public abstract class ShapeV2
{
public IColor _color { get; set; }
public ShapeV2(IColor color) { _color = color; }
public virtual void Print() { Console.WriteLine($"I'm {_color.GetColor()} Shape"); }
}
public class Circle2 : ShapeV2
{
public Circle2(IColor color) : base(color) { }
public override void Print() { Console.WriteLine($"I'm {_color.GetColor()} Circle"); }
}
public class Square2 : ShapeV2
{
public Square2(IColor color) : base(color) { }
public override void Print() { Console.WriteLine($"I'm {_color.GetColor()} Square"); }
}
系统类库举例(代码有缩减):
public class BinaryReader : IDisposable
{
private readonly Stream _stream;
public BinaryReader(Stream input, Encoding encoding, bool leaveOpen)
{
_stream = input;
}
public virtual int Read(byte[] buffer, int index, int count)
{
return _stream.Read(buffer, index, count);
}
}
public class StreamReader : TextReader
{
private readonly Stream _stream;
public StreamReader(Stream stream, Encoding? encoding = null, bool detectEncodingFromByteOrderMarks = true, int bufferSize = -1, bool leaveOpen = false)
{
_stream = stream;
}
public override string ReadToEnd()
{
StringBuilder sb = new StringBuilder(_charLen - _charPos);
do
{
sb.Append(_charBuffer, _charPos, _charLen - _charPos);
_charPos = _charLen; // Note we consumed these characters
ReadBuffer();
} while (_charLen > 0);
return sb.ToString();
}
}
1.2 生成器模式
定义:分步创建复杂对象,可根据配置动态构建
使用注意事项:无固定的代码结构,可以从IBuilder接口扩展多个实现类组合,主要在于形式上的Build过程,最后可调用方法获取结果,也可以在每个步骤内实现
public class Houser
{
public string Make厕所()
{
return new RoomBuilder().Add马桶().Add水龙头().Build();
}
public string Make卧室()
{
return new RoomBuilder().Add床().Add衣柜().Build();
}
}
public class RoomBuilder
{
private StringBuilder _sb = new StringBuilder();
public RoomBuilder Add马桶() { _sb.Append("马桶 "); return this; }
public RoomBuilder Add水龙头() { _sb.Append("水龙头 "); return this; }
public RoomBuilder Add床() { _sb.Append("床 "); return this; }
public RoomBuilder Add衣柜() { _sb.Append("衣柜 "); return this; }
public string Build() { return _sb.ToString(); }
}
系统类库举例(代码有缩减):
var uriBuilder = new UriBuilder {
Scheme = "https",
Host = "example.com",
Port = 8080,
Path = "/api/user",
Query = "id=123&name=test"
};
Uri finalUri = uriBuilder.Uri;
var sb = new StringBuilder();
sb.Append("Hello, ");
sb.Append("World!");
sb.AppendLine();
sb.AppendFormat("Today is {0}.", DateTime.Now);
string result = sb.ToString();
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables()
.AddCommandLine(args);
IConfiguration config = builder.Build();
1.3 责任链模式
定义:对于输入的信息,构建一个处理链条,可以按照联调顺序,逐步处理自己部分的信息,也可以在某个步骤中断执行
特征:自身指向下一链条,并在自身方法执行时调用下一链条方法
public abstract class Filter
{
private Filter _next;
public Filter(Filter next)
{
_next = next;
}
public virtual void Validate(string text)
{
if (_next != null)
_next.Validate(text);
}
}
public class SafeFilter : Filter
{
public SafeFilter(Filter next) : base(next) { }
public override void Validate(string text)
{
if (text.Contains("#"))
throw new Exception();
base.Validate(text);
}
}
public class NullFilter: Filter
{
public NullFilter(Filter next) : base(next) { }
public override void Validate(string text)
{
if (text == null)
throw new Exception();
base.Validate(text);
}
}
public class Runner
{
public void Run()
{
var filter = new SafeFilter(new NullFilter(null));
filter.Validate("11111");
}
}
系统类库举例(代码有缩减):
public delegate Task RequestDelegate(HttpContext context);
public static class LoggingMiddleware
{
public static RequestDelegate UseLogging(RequestDelegate next)
{
return async context =>
{
Console.WriteLine($"[Logging] Request to {context.Request.Path}");
await next(context);
Console.WriteLine($"[Logging] Response: {context.Response.StatusCode}");
};
}
}
public static class AuthenticationMiddleware
{
public static RequestDelegate UseAuthentication(RequestDelegate next)
{
return async context =>
{
if (context.Headers.TryGetValue("Authorization", out var token) && token == "valid-token")
{
Console.WriteLine("[Auth] User authenticated");
await next(context);
}
else
{
Console.WriteLine("[Auth] Unauthorized: Missing or invalid token");
context.Response.StatusCode = 401;
context.Response.Body = "Unauthorized";
}
};
}
}
public static class ResponseMiddleware
{
public static RequestDelegate UseResponse(RequestDelegate next)
{
return async context =>
{
if (string.IsNullOrEmpty(context.Response.Body))
{
context.Response.Body = $"Hello from {context.Request.Path}";
}
Console.WriteLine("[Response] Sending response");
};
}
}
public static class MiddlewarePipeline
{
public static RequestDelegate BuildPipeline()
{
RequestDelegate next = ResponseMiddleware.UseResponse;
next = AuthenticationMiddleware.UseAuthentication(next);
next = LoggingMiddleware.UseLogging(next);
return next;
}
}
public class Program
{
public static async Task Main(string[] args)
{
var pipeline = MiddlewarePipeline.BuildPipeline();
var validRequest = new HttpContext
{
Request = new HttpRequest { Path = "/home" },
Headers = new Dictionary<string, string> { ["Authorization"] = "valid-token" }
};
Console.WriteLine("=== Testing Valid Request ===");
await pipeline(validRequest);
Console.WriteLine($"Result: {validRequest.Response.StatusCode}");
}
}
1.4 享元模式
定义:对于相同属性对象,实际创建一个,所有使用者都共用同一个实例,减少内存消耗
要求:享元对象不可改变,如需改变创建新的并进行赋值
//正常创建树木
public class Tree
{
public int x { get; set; }
public int y { get; set; }
public string Image { get; set; }
public void Display()
{
Console.WriteLine($"在{x},{y}位置显示{Image}");
}
}
public class TreeManager
{
public List<Tree> trees = new List<Tree>();
public void DisplayTrees()
{
foreach (var tree in trees)
tree.Display();
}
}
//享元模式
public class Tree
{
public static void Display(int x,int y,string image)
{
Console.WriteLine($"在{x},{y}位置显示{image}");
}
}
public class TreeManager
{
public void DisplayTrees()
{
Tree.Display(1, 2, "1.jpg");
Tree.Display(1, 2, "1.jpg");
Tree.Display(1, 2, "1.jpg");
}
}
系统类库举例(代码有缩减):
// 字符串字面量自动驻留
string s1 = "hello"; // 首次创建,加入字符串池
string s2 = "hello"; // 直接引用池中的对象(与 s1 共享)
Console.WriteLine(ReferenceEquals(s1, s2)); // 输出:True(同一对象)
// 动态创建的字符串(未驻留)
string s3 = new string('h', 1) + "ello"; // 新建对象(不在池中)
string s4 = "hello";
Console.WriteLine(ReferenceEquals(s3, s4)); // 输出:False(s3 未驻留)
// 显式驻留 s3
string s5 = string.Intern(s3); // 将 s3 加入池(或返回已存在的 "hello")
Console.WriteLine(ReferenceEquals(s5, s4)); // 输出:True(共享池中的对象)
扩展:DDD中的ValueObject(值对象)要求也是不可变,有变化需要创建新的对象
同时DDD中的各层需要做ACL隔离,组件化脱离外部变化,要求每个组件基于自身定义出入参,所以需要组件之间的参数转化
1.5 解释器模式
定义:一种特殊的设计模式,专门针对一类问题进行处理,主要用于定义一个语言的语法,然后构建一个专门解析语法的设计模式
举例代码:

说明:无固定编写语法,只要实现解析功能即可
系统类库举例(代码有缩减):
正则表达式
SQLConnect的连接串解析
IBatis中的sql解析
Lambda表达式等
Json反序列化

1.6 访问者模式
定义:将算法与对象结构分离,不改变结构的情况下新增操作
关键点:
针对较为复杂的对象结构
需要针对对象结构添加一些操作扩展
不要改变对象本身的结构关系
访问者类熟悉对象结构
调用方不熟悉对象结构
实例:如果每新增一个导出方式,就将原有元素添加方法处理,不符合开闭原则
public interface ExportVisitor
{
string Visit(TextElement text);
string Visit(ImageElement text);
string Visit(TableElement text);
}
public interface DocumentElement
{
string Accept(ExportVisitor visitor);
}
public class TextElement : DocumentElement
{
public string GetText() { return "我是文本"; }
public string Accept(ExportVisitor visitor)
{
return visitor.Visit(this);
}
}
public class ImageElement : DocumentElement
{
public string GetPath() { return "图片路径"; }
public string Accept(ExportVisitor visitor)
{
return visitor.Visit(this);
}
}
public class TableElement : DocumentElement
{
public string[,] Content = { { "1","2"},{ "1","2"} };
public string Accept(ExportVisitor visitor)
{
return visitor.Visit(this);
}
}
public class Document
{
private List<DocumentElement> elements = new List<DocumentElement>();
public void AddElement(DocumentElement element) { elements.Add(element); }
public string export(ExportVisitor vistor)
{
StringBuilder sb = new StringBuilder();
foreach(var e in elements)
{
sb.AppendLine(e.Accept(vistor));
}
return sb.ToString();
}
}
public class HtmlExportVisitor : ExportVisitor
{
public string Visit(TextElement text)
{
return $"<p>{text.GetText()}</p>";
}
public string Visit(ImageElement text)
{
return $"<img src={text.GetPath()}/>";
}
public string Visit(TableElement text)
{
return
$"<table>" +
$"<tr>" +
$"<td>{text.Content[0, 0]}</td><td>{text.Content[0, 1]}</td>" +
$"</tr>" +
$"<tr>" +
$"<td>{text.Content[1, 0]}</td><td>{text.Content[1, 1]}</td>" +
$"</tr>" +
$"</table>";
}
}
public class HtmlExportVisitorTest
{
public static void Test()
{
Document doc = new Document();
doc.AddElement(new TextElement());
doc.AddElement(new ImageElement());
doc.AddElement(new TableElement());
var visitor = new HtmlExportVisitor();
Console.WriteLine(doc.export(visitor));
}
}

系统类库举例(代码有缩减):

1.7 原型模式
定义:从一个对象复制出一个新的对象
注意:深浅拷贝的问题
示例:
public class Student : ICloneable
{
public string name { get; set; }
public int age { get; set; }
public object Clone()
{
return new Student()
{
name = this.name,
age = this.age
};
//return JsonConvert.DeserializeObject<Student>(JsonConvert.SerializeObject(this));
}
}
常用场景:
数据库查询出一批数据,需要对数据做处理,还需要使用原来的数据,则可以克隆一份备用,避免重新查询
1.8 备忘录模式
定义:类似游戏存盘,可恢复之前的状态,针对关键对象状态进行保存,便于恢复
场景:文本编辑器的ctrl+z\ctrl+y
系统类库举例(代码有缩减):
using (var graphics = Graphics.FromImage(new Bitmap(200, 200)))
{
// 初始状态:无变换
graphics.FillRectangle(Brushes.White, 0, 0, 200, 200);
// 保存当前状态(生成备忘录:GraphicsState)
GraphicsState stateBeforeTransform = graphics.Save();
// 修改状态:平移坐标系
graphics.TranslateTransform(50, 50);
graphics.FillRectangle(Brushes.Red, 0, 0, 100, 100); // 在平移后的坐标系绘制
// 恢复到保存的状态(使用备忘录)
graphics.Restore(stateBeforeTransform);
// 再次绘制(回到初始坐标系)
graphics.FillRectangle(Brushes.Blue, 0, 0, 100, 100);
}
1.9 中介者模式
定义:通过一个中介层来简化调用者之间的相互调用关系
类比:MQ相互调用对比EventBus消息总线模式
MQ相互调用:发送者必须知道接收者是谁在哪,不同业务场景下还可能不同,每个发送者自己维护调用关系
EventBus消息总线:我只管发不关系接收方的信息维护,EventBus充当中介者来负责把消息转给需要的接受方
示例:
public class ChatUser
{
private ChatRoomMediator ChatRoom { get; set; }
public string IPAddress { get; set; }
public void ChatIn(ChatRoomMediator chatRoom)
{
ChatRoom = chatRoom;
}
public void SendMessage(string msg)
{
ChatRoom.PublishMsg(this, msg);
}
public void ReceiveMessage(string ip,string msg)
{
Console.WriteLine($"{ip}:{msg}");
}
}
public class ChatRoomMediator
{
private List<ChatUser> users = new List<ChatUser>();
public void PublishMsg(ChatUser user,string msg)
{
users.ForEach(x => x.ReceiveMessage(user.IPAddress, msg));
}
}
public class ChatRoomMediatorTest
{
public static void Test()
{
var room = new ChatRoomMediator();
var user1 = new ChatUser();
var user2 = new ChatUser();
user1.ChatIn(room);
user2.ChatIn(room);
user1.SendMessage("hello");
}
}
二、设计模式组合应用
2.1 组合模式应用
一个场景可以由多个设计模式组合使用来解决具体的问题
场景举例:
不同的鸭子实现不同的呱呱叫(策略模式)
把鹅叫也加入进来(适配器模式)
统计鸭子叫声数量(装饰器模式)
创建不同的鸭子(工厂模式)
管理一群鸭子(迭代器模式)
识别具体鸭子的呱呱叫行为(观察者模式)

2.2 组合模式之王-MVC
章节要点:
观察者模式
模型作为被观察者,视图和控制器作为观察者注册监听
当模型状态变化(如BPM值改变),自动通知所有观察者更新(如视图刷新界面)
策略模式
视图将用户输入的处理委托给控制器(如controller.setBPM())
控制器作为视图的策略,可灵活替换(例如DJ控制器替换为心跳控制器)
组合模式
视图内部使用组合结构管理UI组件(如按钮、标签嵌套在面板中),以统一方式处理整体与部分的关系
个人感悟:
个人感觉这个章节的内容不是很好,只是体现思想,并不符合实际编码中对设计模式的使用。不过其实从此章节会延展出作者想表达的一点核心:设计模式是思想,并不局限于形式,而是面对实际问题用什么样的工程实践思想来解决问题
2.3 个人补充-Provider模式
提供者(Provider)模式,由微软提供,此模式由多种设计模式或设计思想组合而成
public interface ILogger
{
void Log(string message);
}
public abstract class LoggerProviderBase : ProviderBase
{
public abstract ILogger CreateLogger();
}
public class ConsoleLoggerProvider : LoggerProviderBase
{
public override ILogger CreateLogger()
{
return new ConsoleLogger();
}
}
public class ConsoleLogger : ILogger
{
public void Log(string message)
{
Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] [Console] {message}");
}
}
public class FileLoggerProvider : LoggerProviderBase
{
private readonly string _filePath;
public FileLoggerProvider(string filePath)
{
_filePath = filePath ?? throw new ArgumentNullException(nameof(filePath));
}
public override ILogger CreateLogger()
{
return new FileLogger(_filePath);
}
}
public class FileLogger : ILogger
{
private readonly string _filePath;
public FileLogger(string filePath)
{
_filePath = filePath;
Directory.CreateDirectory(Path.GetDirectoryName(_filePath));
}
public void Log(string message)
{
File.AppendAllText(_filePath, $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] [File] {message}{Environment.NewLine}");
}
}
public class LoggerProviderConfiguration
{
public string ProviderName { get; set; } = "Console";
public string FilePath { get; set; } = "logs/app.log";
}
public class LoggerProviderFactory
{
private readonly LoggerProviderConfiguration _config;
public LoggerProviderFactory(IOptions<LoggerProviderConfiguration> configOptions)
{
_config = configOptions.Value;
}
public LoggerProviderBase CreateProvider()
{
return _config.ProviderName.ToLower() switch
{
"file" => new FileLoggerProvider(_config.FilePath),
"console" => new ConsoleLoggerProvider(),
_ => throw new ArgumentException($"不支持的日志提供者:{_config.ProviderName}")
};
}
}
static void Main(string[] args)
{
var hostBuilder = new HostBuilder()
.ConfigureAppConfiguration(
(_, config) =>
{
config.AddJsonFile("appsettings.json", optional: false);
})
.ConfigureServices((context, services) =>
{
services.Configure<LoggerProviderConfiguration>(
context.Configuration.GetSection("LoggerProvider")
);
services.AddSingleton<LoggerProviderFactory>();
services.AddTransient<ILogger>(
provider =>
{
var factory = provider.GetRequiredService<LoggerProviderFactory>();
var loggerProvider = factory.CreateProvider();
return loggerProvider.CreateLogger();
});
});
using var host = hostBuilder.Build();
var logger = host.Services.GetRequiredService<ILogger>();
logger.Log("这是一条测试日志!");
logger.Log("Provider 模式演示成功!");
Console.ReadLine();
}
总结下:
策略模式:多种Provider
工厂模式:工厂根据配置创建具体的Provider
解释器模式:appsettings.json解析
桥接模式:Factory工厂与Configuration解绑
单例模式:DI容器添加单例工厂、配置等
创建者模式:Host进程创建由Builder模式构建
三、现实中的设计模式
3.1 书中观点
设计模式定义:模式是在某个上下文中针对某个问题的解决方案
上下文:模式的适用场景,并且此种场景会不断的出现
问题:代之你要解决的目标
解决方案:一种通用的设计规范,大部分人都能快速认同理解,并选择相同的方案
举例:
问题:我怎么能准时上班?
上下文:我把钥匙丢在车内了
解决方案:打破窗户,进入车内,开车上班
章节关键信息点:
设计模式不是法律或者准则,只是指导方针,可以修改写法去适应你的问题
可以创造设计模式吗?模式是被发现的,不是被创建的,如果发现某个领域里重复出现一个问题,并被你使用一个方案解决,也可以认为是创造设计模式(可以找三个程序员,如果他们都认同,就是通用的设计模式方案)
用模式思考问题:
保持简单
设计模式不是灵丹妙药,实际它什么都不是
知道什么时候需要设计模式
重构时间就是模式时间
拿掉不需要的,不要害怕移除设计模式
如果现在不需要就别做
模式的心智:
初学者单纯使用模式
中阶者思考什么时候需要设计模式,什么时候不需要
悟道者能够看到模式的自然融入
反模式:一开始认为是好模式,但在长期维护后发现是一个坏模式,通过反模式归档,帮助其他人借鉴避坑
3.2 个人观点
基于当下做决策,不要依赖自己的想象去构建代码
技术类的组件,多用设计模式,因为其特点是你发明的,你可以清楚的知道它的使用模式和形态
业务需求类的,尽量减少设计模式的使用,因为其特征是异变的,可能需求的提出者都无法料到下次修改的点是什么
设计模式的作用是把一个组件做好,我们日常面对的问题是如何定义这个组件,所以如果业务能够做到标准产品化,可以基于设计模式来构建业务逻辑
局部使用,不要针对大的流程节点设计模式,细节多异变,更多的设计组件+衔接运转,不要把整个流程拿来做抽象
学习设计模式最好的方式就是阅读源码