前言
大家熟知的GOF23种设计模式,源自《Design Patterns: Elements of Reusable Object-Oriented Software》一书,由 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 合著,四人组Gang of Four简称GOF!总结了在面 向对象语言开发过程中常见问题的解决方案! 设计模式是面向对象语言开发过程中,遇到的种种场景和问题,然后提出的思 路和解决方案,最后沉淀下来,就成了设计模式--- 有具体招数可循; 设计模式其实就是解决问题的思路,是前辈总结出来的有效方式方法,就是套 路! 学习设计模式,就是为了站在前辈的肩膀上,能更快捷优雅的解决面向对 象程序开发设计问题。
设计模式分类
三大类
- 创建型设计模式
-
- 关注对象的创建
- 行为型设计模式
-
- 关注类与类之间的关系
- 结构型设计模式
-
- 关注对象和行为的分离
套路
设计模式六大原则与23种设计模式的关系 设计模式是解决问题的具体套路,降龙十八掌 六大原则是程序设计的指导原则,道家的小无相功, 没有具体招数,没有具体的标准。
类和类之间的关系
单个类没有任何意义!面向对象决定了类和类之间的各种关系!
横向:平级关系
- 组合关系 :
组合也是关联关系的一种特例,它体现的是一 种contains-a的关系,这种关系比聚合更强,也称为强聚合。 例如:人与心脏
- 聚合关系(Aggregation):
表示的是整体和部分的关系, 整体与部分是可以分开(has-a)。
例如:车和发动机
- 关联关系(Association):
两个类之间语义级别的一种强依 赖关系。
例如:班级与学生、球员与球队
- 依赖关系(Dependence):
假设A类的变化引起了B类的变 化,则说名B类依赖于A类。---方法里面/参数,是一种很淡 的关联---单向的依赖
纵向:层级关系
- 继承
-
- 强继承:任何子类继承父类,都必须拥有父类的一切行为 和属性
- 实现
-
- 强实现:子类实现接口,必须实现接口的方法
关系:非常紧密
类和类的强弱关系
继承≈实现 >组合>聚合>关联>依赖
结构型设计模式常规套路:组合优于继承!
结构型设计模式
7种结构型设计模式,关注类与类之间的关系 其实就是折腾组合与继承,(组合优于继承) 为程序提供更好的灵活性和扩展性。
Adapter Pattern(适配器)
将一个接口转换成客户希望的另一个接口,使接口不兼容的 那些类可以一起工作,其别名为包装器(Wrapper)。
场景:
现在很多操作几乎是重复的调用API和自己封装好API,例如数据库操作,都是CRUD操作,但是如果要进行优化,是用了第三方库的时候,方法名字是和自己封装的名字是对应不上的,但是也想要使用,所以需要适配器类才能让他和目前的方法进行适配。
现存已有的方法:
public interface IHelper
{
void Add<T>();
void Delete<T>();
void Update<T>();
void Query<T>();
}
需要适配的方法:
public class RedisHelper
{
/// <summary>
/// 第三方提供的sdk
/// </summary>
public RedisHelper()
{
Console.WriteLine("构造RedisHelper");
}
public void AddRedis<T>()
{
Console.WriteLine($"This is {this.GetType().Name} AddRedis");
}
public void DeleteRedis<T>()
{
Console.WriteLine($"This is {this.GetType().Name} DeleteRedis");
}
public void QueryRedis<T>()
{
Console.WriteLine($"This is {this.GetType().Name} QueryRedis");
}
public void UpdateRedis<T>()
{
Console.WriteLine($"This is {this.GetType().Name} UpdateRedis");
}
}
类适配器
原理:继承第三方库和现有的方法接口,然后可以实现接口,调用第三方库的方法
缺点:由于继承了第三方类库,就导致拥有了第三方类库的方法,这样的话不安全,具有一定的侵入性
public class RedisClassAdapter : RedisHelper, IHelper
{
public void Add<T>()
{
base.AddRedis<T>();
}
public void Delete<T>()
{
base.DeleteRedis<T>();
}
public void Query<T>()
{
base.QueryRedis<T>();
}
public void Update<T>()
{
base.UpdateRedis<T>();
}
}
使用方法:
IHelper helper = new RedisClassAdapter();
helper.Add<Program>();
helper.Delete<Program>();
helper.Update<Program>();
helper.Query<Program>();
对象适配器
原理:在类中,写一个需要适配的类的实例化,然后实现接口,调用实例化中的方法。
建议使用对象适配器,能够防止方法泄露,而且可以复用,根据构造函数进行传值可以多种类一起使用。
/// <summary>
/// Redis 对象适配器
/// </summary>
public class RedisObjectAdapter : IHelper
{
private readonly RedisHelper redisHelper = new RedisHelper();
//public RedisObjectAdapter(RedisHelper _redisHelper)
//{
// redisHelper = _redisHelper;
//}
public void Add<T>()
{
redisHelper.AddRedis<T>();
}
public void Delete<T>()
{
redisHelper.DeleteRedis<T>();
}
public void Query<T>()
{
redisHelper.QueryRedis<T>();
}
public void Update<T>()
{
redisHelper.UpdateRedis<T>();
}
}
总结:
适合做重构和接入新的东西。
对象适配器 > 类适配器
Bridge(桥接)
Composite(组合)
Decorator(装饰)
原理:组合+继承~~ 完成层层装饰~~
代码:
基础抽象类:
public abstract class AbstractStudent// : Object
{
public int Id { get; set; }
public string Name { get; set; }
public abstract void Study();
}
继承抽象类代码:
public class StudentVip : AbstractStudent
{
/// <summary>
/// 付费 上课前要预习
/// 上课学习
/// </summary>
public override void Study()
{
Console.WriteLine("{0} is a vip student studying .net Vip", Name);
}
}
装饰器类:继承基础抽象类,证明这个装饰类也是学生类
public abstract class AbstractDecorator : AbstractStudent// 装饰后还是一个学生
{
private AbstractStudent _Student = null;
public AbstractDecorator(AbstractStudent student) : base()
{
_Student = student;
}
public override void Study()
{
//Console.WriteLine("***********************");
_Student.Study();
//Console.WriteLine("***********************");
}
}
装饰器若干:
可以写多种修饰器代码,然后执行父类的Study方法之后写入自己的修饰。
public class StudentDecoratorVideo : AbstractDecorator
{
public StudentDecoratorVideo(AbstractStudent student) : base(student)
{
}
public override void Study()
{
base.Study();
Console.WriteLine("获取视频代码。。。");
}
}
若干:。。。。。。。。。。
使用代码:
AbstractStudent student = new StudentVip()
{
Id = 123,
Name = "小海"
};
// 装饰的顺序不一样 执行也不一样
// 多个装饰 随意修饰
student = new StudentDecoratorVideo(student);
student = new StudentDecoratorAnswer(student);
student = new StudentDecoratorHomework(student);
student.Study();
效果:
Facade(外观/门面)
Flyweight(享元)
Proxy(代理)
给某一个对象提供一个代理对象,并由代理对象控制对原对 象的引用。
例如:
- 地产代理
- 火车票代售点
- 翻墙梯子
- VPN
代码:
业务接口:
public interface ISubject
{
/// <summary>
/// get
/// </summary>
/// <returns></returns>
bool GetSomething();
/// <summary>
/// do
/// </summary>
void DoSomething();
}
现有的原本类的方法:
public class RealSubject : ISubject
{
public RealSubject()
{
Thread.Sleep(2000);
long lResult = 0;
for (int i = 0; i < 100000000; i++)
{
lResult += i;
}
Console.WriteLine("RealSubject被构造。。。");
}
/// <summary>
/// 火车站查询火车票
/// </summary>
public bool GetSomething()
{
Console.WriteLine("坐车去火车站看看余票信息。。。");
Thread.Sleep(3000);
Console.WriteLine("到火车站,看到是有票的");
return true;
}
/// <summary>
/// 火车站买票
/// </summary>
public void DoSomething()
{
//Console.WriteLine("This is DoSomething Before");
//try
//{
Console.WriteLine("开始排队。。。");
Thread.Sleep(2000);
Console.WriteLine("终于买到票了。。。");
//}
//catch (Exception)
//{
// throw;
//}
}
}
代理模式,将现有的类包一层代码:需要使用一个缓存类库
public class ProxySubject : ISubject
{
/// <summary>
/// RealSubject只实例化一次,单例代理
/// </summary>
private static ISubject _iSubject = new RealSubject();
/// <summary>
/// 缓存代理--提升性能
/// 查询票
/// </summary>
/// <returns></returns>
public bool GetSomething()
{
//bool bResult = _iSubject.GetSomething();
//return bResult;
string key = $"{nameof(ProxySubject)}_{nameof(GetSomething)}";
bool bResult = false;
if (CustomCache.Exist(key))
{
bResult = CustomCache.Get<bool>(key);
}
else
{
bResult = _iSubject.GetSomething();
CustomCache.Add(key, bResult);
}
return bResult;
return _iSubject.GetSomething();
}
/// <summary>
/// 日志--异常处理--只要通过Proxy来调用 就能获得这两个功能
/// 买票
/// </summary>
public void DoSomething()
{
try
{
Console.WriteLine("This is DoSomething Before");
_iSubject.DoSomething();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
throw;
}
}
}
缓存类库:用来判断这个代理有没有加入
public class CustomCache
{
private static Dictionary<string, object> CustomCacheDictionary = new Dictionary<string, object>
();
public static void Add(string key, object value)
{
CustomCacheDictionary.Add(key, value);
}
public static T Get<T>(string key)
{
return (T)CustomCacheDictionary[key];
}
public static bool Exist(string key)
{
return CustomCacheDictionary.ContainsKey(key);
}
}
总结:
使用情况:
可以增加一些公共的业务逻辑,而不修改历史代码,可以在原来的逻辑之前或者之后进行增加新的业务。
结构型设计模式:
通过新的类来进行包装; 包一层大法; 没有任何问题是包一层不能解决的,如果不能解决,再包一层;
行为型设计模式
11种行为型设计模式,是最大 的一个家族了。 行为型设计模式关注的是对象和 行为的分离,直白点说就是行为(方 法)是放在这个类里面,还是那个类 里面,关注的内容更细腻,因此套 路也更多!
核心:做行为的转移,做甩锅。
Interpreter(解释器)
Template Method(模板方法)
Chain of Responsibility(责任链)
使多个对象都有处理请求的机会,从而避免了请求的发送者和接收 者之间的耦合关系,将这些对象串成一条链,并沿着这条链一直传 递该请求,直到有对象处理它为止。 行为型设计模式的巅峰之作,无止境的行为封装转移!
场景模拟:
请假-审批流程
Context上下文环节,行为型设计模式的标配
因为行为型模式是关注对象和行为的分离 也就是把方法转移来转移去 那些参数信息和中间结果就得存一下 这就是Context了
审批业务逻辑转移 :
上下文保存变量类:
public class ApplyContext
{
public int Id { get; set; }
public string Name { get; set; }
/// <summary>
/// 请假时长
/// </summary>
public int Hour { get; set; }
public string Description { get; set; }
public bool AuditResult { get; set; }
public string AuditRemark { get; set; }
}
基础业务逻辑:
很明显的问题:面向过程编程,完全没有封装。。
Console.WriteLine("找项目经理审批");
if (context.Hour <= 8)
{
Console.WriteLine("PM审批通过");
}
Console.WriteLine("找主管审批");
if (context.Hour <= 16)
{
Console.WriteLine("主管审批通过");
}
else
{
//..... 继续往后去找人审批
Console.WriteLine("************************************");
}
封装-继承-多态
把每层封装为一个类:相同的写一个基类里面
public abstract class AbstractAuditor
{
public int Id { get; set; }
public string Name { get; set; }
// 因为必须有此方法 所以必须是抽象方法abstract修饰
public abstract void Audit(ApplyContext context);
/// <summary>
/// 下一个审批者
/// </summary>
protected AbstractAuditor _NextAuditor;
/// <summary>
/// 用作设置下一个审批者
/// </summary>
/// <param name="nextAuditor">下一个审批者</param>
public void SetNextAuditor(AbstractAuditor nextAuditor)
{
_NextAuditor = nextAuditor;
}
}
PM类:
public class PM : AbstractAuditor
{
public override void Audit(ApplyContext context)
{
if (context.Hour <= 8)
{
context.AuditResult = true;
context.AuditRemark = "PM审批通过";
}
else
{
_NextAuditor.Audit(context); //把新一个要审批的行为转移走了;
}
}
}
..........省略一百层。。。。
使用:
设置每一层的人员,并且在上层自己设置下一级是哪个人。
AbstractAuditor pM = new PM()
{
Id = 123,
Name = "其乐无穷"
};
AbstractAuditor charge = new Charge() //请假者自己去找主管审批
{
Id = 234,
Name = "诺"
};
AbstractAuditor manager = new Manager()
{
Id = 345,
Name = "麦子熟了"
};
pM.SetNextAuditor(charge);
charge.SetNextAuditor(manager);
设置完下一层的时候 执行下审批
pM.Audit(context);
从POP到OOP的升级
从菜鸟走向中级开发
使用场景:
如果一个流程有很多个环节,业务由其中一个或者多个协作处理, 就可以选择责任链模式!
- 流程审批
- 工资结算
- 管道处理模型
总结:
甩锅大法~~ 为了保证自己的稳定; 在这个类中不去固定某一个 类型,而是让别人来执行;
Command(命令)
Iterator(迭代器)
Mediator(中介者)
Memento(备忘录)
Observer(观察者)
业务背景:
一只神奇的猫
猫叫一声之后触发 一系列的行为:
1、小孩儿哭了
2、兄弟醒了
3、狗叫了
4、爸爸生气了~·
5、邻....
要求:以面向对象的想来模拟当前的这个场景:
代码:
基础代码:
很明显,Miao的时候确实都触发了,但是职责太多,猫应该只做自己的事情,其他的动作甩给上层处理
public void Miao()
{
Console.WriteLine("{0} Miao.....", this.GetType().Name);
new Mouse().Run();//猫和老鼠是啥关系--依赖
new Chicken().Woo();
new Baby().Cry();
new Brother().Turn();
new Dog().Wang();
new Father().Roar();
new Mother().Whisper();
new Neighbor().Awake();
new Stealer().Hide();
}
修改为观察者模式OOP:
public List<IObserver> observerlist = new List<IObserver>();
public void MiaoObserver()
{
Console.WriteLine("{0} MiaoObserver.....", this.GetType().Name);
foreach (var observer in observerlist) //具体执行的是什么玩意, 猫不关注; 集合中有什么行为,就执行什么行为;
{
observer.Action();
}
}
IObserver接口:
/// <summary>
/// 只是为了把多个对象产生关系,方便保存和调用
/// 方法本身其实没用
/// </summary>
public interface IObserver
{
void Action();
}
使用:
已经把刚才的Miao给拆开,而且做到了任务单一,把一些任务甩给调用层,上一层
Cat cat = new Cat();
cat.observerlist.Add(new Baby());
cat.observerlist.Add(new Brother());
cat.observerlist.Add(new Chicken());
cat.observerlist.Add(new Dog());
cat.observerlist.Add(new Father());
cat.observerlist.Add(new Mother());
cat.MiaoObserver();
修改为委托实现:
public Action ActionHander;
public void MiaoDelegate()
{
Console.WriteLine("{0} MiaoDelegate.....", this.GetType().Name);
ActionHander?.Invoke();
}
委托使用:
Cat cat = new Cat();
cat.ActionHander += new Baby().Action;
cat.ActionHander += new Brother().Action;
cat.ActionHander += new Chicken().Action;
cat.ActionHander += new Dog().Action;
cat.ActionHander += new Father().Action;
cat.ActionHander += new Mother().Action;
cat.ActionHander += new Cat().MiaoDelegate;
//cat.ActionHander.Invoke(); //可以直接执行委托
cat.MiaoDelegate();
注意:
本质OOP和委托没有区别
OOP: 把一堆的对象保存起来
委托:把方法包装到委托中去了------委托的本质:其实是一个类;
修改为事件:
public event Action ActionHanderEvent;
public void MiaoEventHander()
{
Console.WriteLine("{0} MiaoEventHander.....", this.GetType().Name);
ActionHanderEvent?.Invoke();
}
事件使用:
Cat cat = new Cat();
cat.ActionHanderEvent += new Baby().Action;
cat.ActionHanderEvent += new Brother().Turn;
cat.ActionHanderEvent += new Chicken().Woo;
cat.ActionHanderEvent += new Dog().Wang;
cat.ActionHanderEvent += new Father().Roar;
cat.ActionHanderEvent += new Mother().Whisper;
cat.ActionHanderEvent += new Cat().MiaoDelegate;
//cat.ActionHanderEvent.Invoke(); //不允许的
//cat.MiaoEventHander();
委托和事件的区别:
事件---执行的执行,只允许在当前定义时间的这个类里面执行,不允许在外面执行,就是子类都不能执行;
搭建框架的时候,需要一些权限的管控; WPF 控件 Winform 控件里面;+= 方法的时候,都是事件,而不是委托;
State(状态)
Strategy(策略)
Visitor(访问者)
创建型设计模式
6种创建型设计模式,关注对象的创建, 其实就是如何new一个对象的问题, 这里也是有非常多的套路
Singleton(单例)
介绍:
就是整个程序有且仅有一个实例。该类负责创建自己的对象, 同时确保只有一个对象被创建。 自己创建自己,保证在进程中只有这个一个对象; 结构最简单的设计模式,没有之一。
单例:双判断+锁
业务背景:
有些时候重复New对象导致重置对象或者说浪费内存,则需要单例模式,但是由于多线程的时候
代码:
public class SingletonBase<T> where T : class, new()
{
private static T _Instance;
private static object ObjLock = new object();
/// <summary>
/// 双判断+锁
/// </summary>
/// <returns></returns>
public static T CreateInstanc()
{
if (_Instance == null)
{
// 由于考虑到多线程的线程安全问题 所以使用加锁 方式处理
lock (ObjLock)
{
if (_Instance == null)
{
_Instance = new T();
}
}
}
return _Instance;
}
}
静态构造函数 单例:
静态构造函数:特点
在整个进程中,执行且只执行一次的;
所以在静态函数中返回单例。
代码:
private SingletonSeoncd()
{
long lResult = 0;
for (int i = 0; i < 10000000; i++)
{
lResult += i;
}
Thread.Sleep(2000);
Console.WriteLine($"{GetType().Name}被构造一次 {Thread.CurrentThread.ManagedThreadId}");
}
/// <summary>
/// 静态构造函数:特点
/// 在整个进程中,执行且只执行一次的;
/// </summary>
static SingletonSeoncd()
{
_SingletonSeoncd = new SingletonSeoncd();
}
public static SingletonSeoncd CreateInstance()
{
return _SingletonSeoncd;
}
// 静态类属性 返回
private static SingletonSeoncd _SingletonSeoncd;
public int Id { get; set; }
public string Name { get; set; }
public static void Show()
{
Console.WriteLine(" Show 方法~~");
}
初始化的时候声明静态变量 单例:
public static SingletonThird CreateInstance()
{
return _SingletonThird;
}
private static SingletonThird _SingletonThird = new SingletonThird();
应用场景:
线程池、数据库连接池、配置文件对象、IOC容器实例 等需要单例的
New个对象不费什么事儿 请不要画蛇添足,没有必须单例的,请勿单例!
总结:
- 私有化构造函数
- 对外提供获取实例的方法
- 提供静态变量,实例重用
Factory Method(工厂方法)
介绍:
通过定义工厂父类负责定义创建对象的公共接口, 而子类则负责生成具体的对象。 每一个不同的的对象分别自定义个工厂+抽象;
Abstract Factory(抽象工厂)
介绍:
在抽象工厂模式中,接口是负责创建一个相关对象的工厂。 每个生成的工厂都能按照工厂模式提供对象。 工厂+约束!
Builder(建造者)
介绍:
是将一个复杂的对象的构建与它的表示分离, 使得同样的构建过程可以创建不同的表示, 创建者模式隐藏了复杂对象的创建过程。
核心: 就是灵活应用;
有之前的审批流程代码:
由于也需要修改代码进行改变组织架构,所以引进建造者模式,进行灵活使用
代码:
IBuilder接口:
/// <summary>
/// 建造者;可能需要创建很多对象,然后要组装;
/// </summary>
public interface IBuilder
{
public AbstractAuditor CreateCharge();
public AbstractAuditor CreateManager();
//建造者:最终要建造一个审批的开始节点;
public AbstractAuditor Build();
}
每一个新的建造者:
public class AuditorWorkFlowsBuild : IBuilder
{
public AbstractAuditor Build()
{
AbstractAuditor pm = new PM();
AbstractAuditor charge = CreateCharge();
AbstractAuditor manager = CreateManager();
pm.SetNextAuditor(charge);
charge.SetNextAuditor(manager);
return pm;
}
public AbstractAuditor CreateCharge()
{
return new Charge();
}
public AbstractAuditor CreateManager()
{
return new Manager();
}
}
使用:
说明:
可以写多个建造者,所以在切换组织的时候,建造者切换即可。
//如果组织架构发生变化;
//审批历程发生改变; 可以直接换掉建造者;
IBuilder builder = new AuditorWorkFlowsBuild();
AbstractAuditor pm = builder.Build();
pm.Audit(context);
Prototype(原型)
介绍:
是用于创建重复的对象,同时又能保证性能。专门用来批量生产对象!
原理:
既要快速获得对象实例 又要对象直接不能互相干扰
copy大法:
MemberwiseClone
代码:
在创建实例的时候,不用返回上一个实例,直接复制一个即可,这样既能保证实例是最新的,也能保证快。
public static SingletonPrototype CreateInstance()
{
//MemberwiseClone: 内存拷贝; 不是浅拷贝
object oInstance = _SingletonPrototype.MemberwiseClone();
return (SingletonPrototype)oInstance;
}
private static SingletonPrototype _SingletonPrototype = new SingletonPrototype();
创建对象的方式:
- new()
- 克隆
- 反序列化
- 反射
Simple Factory(简单工厂)
介绍:
是通过专门定义一个类来负责创建其他类的实例, 被创建的实例通常都具有共同的父类。
业务背景:
继续请假审批流程:
由于使用建造者的时候,修改建造者也比较繁琐,所以进一步优化,改为配置文件即可。
代码:
/// <summary>
/// 这个工厂是为了来创建建造者的
/// </summary>
public class SimpBuidlerFactory
{
// 建造者的dll库
private static string dllName = "XH.DesignPattern.ResponsibilityChainPattern.dll";
// 建造者的类名:可以包装为配置文件 修改配置文件即可
private static string typeName = "XH.DesignPattern.ResponsibilityChainPattern.Builder.AuditorWorkFlowsBuild";
public static IBuilder CreateBuilderInstance()
{
//字符串: 写到配置文件中去;
// 修改配置文件的信息,不用重新编译,可以直接生效
//return new AuditorWorkFlowsNewBuild();
//反射
Assembly assembly = Assembly.LoadFrom(dllName); // 获取DLL
Type type = assembly.GetType(typeName);// 获取DLL中的当前文件类型
object oinstance = Activator.CreateInstance(type);// 创建对象
return oinstance as IBuilder;
}
}
使用:
IBuilder builder = SimpBuidlerFactory.CreateBuilderInstance();
AbstractAuditor pm = builder.Build();
pm.Audit(context);
总结:
结果: 如果组织架构发生改变; 只需要实现新的建造者,然后修改配置文件即可,根本就不用去修改历史代码;
这才是设计模式的正确使用方式;
设计模式总结:
结构型:包一层
行为型:甩锅大法
创建型:灵活运用
设计模式真的应该分3大类,创建型-结构型-行为型,就三类 招数
所以,23种设计模式,大家不要着相,不用去背, 其实学会三个核心套路,见招拆招,有问题不要想着先用什 么设计模式,先看看要做到什么,然后用什么核心套路,然 后根据特点微调,设计模式应该是自然而然 设计模式不是完美的,是为了解决一类问题而存在,通常在 解决一类问题时,还会带来其他问题,设计者要做的就是扬 长避短,比如设计模式融合,比如其他技术手段,最终解决 问题---程序设计,其实就是走一步看一步,不是一蹴而就