我学设计模式的第一本书是“大话设计模式”,博客记录小笔记与心得吧
设计模式其实是面向对象编成的产物,学会了这些思想,让代码变得更优美,解藕更能适应负责的业务变更。
场景描述:制作一个计算器。
1.实现加减乘除。
2.以后有其它算法的时候,容易维护。
工厂类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace OperationByFactory.Class { //运算工厂类 class OperationFactory { public static Operation createOperation(string operate) { Operation oper = null; switch (operate) { case "+": oper = new OperationAdd(); break; case "-": oper = new OperationSub(); break; case "*": oper = new OperationMul(); break; case "/": oper = new OperationDiv(); break; } return oper; } } }
运算类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace OperationByFactory.Class { //运算类 class Operation { private double _numberA = 0; private double _numberB = 0; public double NumberA { get { return _numberA; } set { _numberA = value; } } public double NumberB { get { return _numberB; } set { _numberB = value; } } public virtual double GetResult() { double result = 0; return result; } } }
加减乘除类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace OperationByFactory.Class { //加法类 class OperationAdd:Operation { public override double GetResult() { return NumberA + NumberB; } } } //减法类 11 class OperationSub:Operation 12 { 13 public override double GetResult() 14 { 15 return NumberA - NumberB; 16 } 17 } { //乘法类 class OperationMul:Operation { public override double GetResult() { return NumberA * NumberB; } } } { //除法类 class OperationDiv:Operation { public override double GetResult() { if(NumberB== 0) throw new Exception("除数不能为0"); return NumberA / NumberB; } } }
小结:简单工厂模式很简单,重要的是。这个设计模式是怎么一步步形成的、和这样做的好处有哪些。
1.可移植性号,无论是控制台程序,Windows程序,Web程序,都可以用这段代码。
2.扩展性好,更安全,以后增加平方,立方,开根号等运算的时候,增加一个相应的类,然后再Switch里增加分支就好了。同时也不用担心程序员修改原先写好的加减乘除类,使得原先的代码不会被有意或者无意的修改,所以更安全。
3.(1)编程尽可能避免重复的代码。(2)对业务进行封装,尽可能的让业务逻辑和页面逻辑分开,让它们的耦合度下降,这样更容易维护和扩展。
4.大家可以熟悉一下UML类图,画出来之后理解更直观。
单例模式是就是在系统运行时,我们希望类生成的对象就一个,类实例化只能时一样,比如线程池、缓存等,我们在系统运行如果缓存或线程池变化,可能会造成系统出现异常,资源过度浪费。
单例模式的定义:
确保一个类只能有一个实例,并提供一个全局访问点。
单例模式分为两种,一种是不安全的单例模式(我们需要废弃),第二种是线程安全的单例模式。下面列举几种单例模式的实现。
1。饿汉式 2。同步方法-懒汉式 3。同步代码块-懒汉式 4。双重检查锁 5。枚举类 6。静态内部类 7。静态枚举类 --- 使用单例模式注意三大条件: 1.私有化构造器, 2.类含有静态私有对象, 3.提供公共静态的方法创建或获取私有对象的值。 --- 使用单例 优点: 1.在内存中只有一个实例对象,减少频繁创建销毁对象实例的内存开销; 2.避免对资源的过度占用。 缺点: 没有接口,不能继承,与单一职责原则冲突,一个类只应该关系内部逻辑而不应该关系外部怎样实例化。 使用场景: 1.唯一的序列号 2.WEB中的计数器 3.创建一个对象消耗的资源过多
/** * 单例模式:饿汉式 */ public class SingletonDemo01 { //类初始化立即加载,由于类加载时完成初始化,线程安全。不用同步快,调用效率高。 private static SingletonDemo01 instance = new SingletonDemo01(); private SingletonDemo01() { } public static SingletonDemo01 getInstance() { return instance; } } /** * 单例模式:懒汉式线程安全 */ public class SingletonDemo03 { //类初始化延迟加载,方法同步线程安全,调用效率低。 private static SingletonDemo03 instance = null; private SingletonDemo03() { } public static synchronized SingletonDemo03 getInstance() { if (instance == null) { instance = new SingletonDemo03(); } return instance; } } /** * 单例模式:懒汉式双重检查锁 */ public class SingletonDemo04 { //由于编译器优化和JVM内部模型原因,同步块会有问题,会出现问题 private static SingletonDemo04 instance = null; private SingletonDemo04() { } public static SingletonDemo04 getInstance() { if (instance == null) { synchronized (SingletonDemo04.class) { if (instance == null) { instance = new SingletonDemo04(); } } } return instance; } } /** * 单例模式:静态内部类懒加载 */ public class SingletonDemo05 { //外部没有static属性,不会立即加载对象 //真正调用getInstance,才会加载内部类 //高效+安全+延迟加载 private static class SingletonInstance { public static SingletonDemo05 instance = new SingletonDemo05(); } private SingletonDemo05() { } public static SingletonDemo05 getInstance() { return SingletonInstance.instance; } } /** * 单例模式:枚举类 */ public enum SingletonDemo06 { //直接用SingletonDemo06.INSTANCE本身代理,代表一个对象 //避免反射、反序列化,效率高 //没有懒加载 INSTANCE; Enum ss; } /** * 单例模式:枚举类实现懒加载 */ public class SingletonDemo08 { // 私有构造函数 private SingletonDemo08() { } public static SingletonDemo08 getInstance() { return Singleton.INSTANCE.getInstance(); } private enum Singleton { INSTANCE; private SingletonDemo08 singleton; // JVM保证这个方法绝对只调用一次 Singleton() { singleton = new SingletonDemo08(); } public SingletonDemo08 getInstance() { return singleton; } } } import java.io.ObjectStreamException; import java.io.Serializable; /** * 单例模式:解决反射漏铜,通过设置constructor.setAccessible(true);//跳过权限校验,是的创建实例不一致 * 解决序列化漏铜,readResolve() */ public class SingletonDemo07 implements Serializable { //类初始化延迟加载,方法同步线程安全,调用效率低。 private static SingletonDemo07 instance = null; private SingletonDemo07() { if (instance != null) {//防止通过反射破解单例 throw new RuntimeException(); } } public static synchronized SingletonDemo07 getInstance() { if (instance == null) { instance = new SingletonDemo07(); } return instance; } //反序列化时,直接返回此对象,而不生成新对象 private Object readResolve() throws ObjectStreamException { return instance; } }
场景描述:写一个给人搭配不同服饰的系统,类似QQ秀,或者游戏皮肤之类的。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DressByDecorator { //Person类 class Person { public Person() { } private string name; public Person(string name) { this.name = name; } public virtual void Show() { Console.WriteLine("装扮的{0}",name); } } } 服饰类,继承Person类 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DressByDecorator { //服饰类 class Finery : Person { protected Person componnet; //打扮 public void Decorate(Person component) { this.componnet = component; } public override void Show() { if (componnet != null) { componnet.Show(); } } } } 鞋子类,继承服饰类。 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DressByDecorator { //鞋子类 class Shose:Finery { public override void Show() { Console.WriteLine("鞋子"); base.Show(); } } } Main函数实现 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DressByDecorator { //Person类 class Person { public Person() { } private string name; public Person(string name) { this.name = name; } public virtual void Show() { Console.WriteLine("装扮的{0}",name); } } }
1.观察者模式介绍
观察者模式又叫发布-订阅模式,它定义了对象间的一种一对多关系,当一个对象的状态发生改变时,所有依赖于它的对象都会收到通知并被自动更新。观察者模式就四个角色:抽象主题,具体主题,抽象观察者,具体观察者。抽象主题是一个抽象的接口或者抽象类,对主题的功能进行抽象,抽象观察者对具体的观察者进行抽象。观察者模式在软件开发中的应用十分广泛,如微信订阅号、微博订阅等都采用了观察者模式,我们关注了某个明星的微博,当这个明星更新微博状态时我们的微博都会收到通知,明星的微博就是主题角色,我们的微博属于观察者角色。
下边通过大话设计模式中秘书做卧底的例子来理解观察者模式的用法。上班时间有的同事喜欢偷偷看股票行情,有的同事看NBA直播,但是老板会不定时来办公室,如果被老板逮到就不好了,于是大家想出来一个办法:让秘书小妹在外边把风,如果老板来了就通知下大家,这样就不会被逮到了。这个栗子中秘书小妹就是一个主题,同事们属于观察者,秘书小妹如果看到老板过来就会通知 送她零食的同事们。代码比较简单:
//抽象主题类 public interface ISubject { //添加观察者 送零食的加进来,老板来了通知你 void Add(Observer observer); //删除观察者 不送零食的秘书小妹就不通知了 void Remove(Observer observer); //主题状态 string SubjectState { get; set; } //通知方法 void Notify(); } //具体主题 ,秘书类 public class Mishu : ISubject { //秘书要知道通知哪些同事 private IListobservers = new List (); public void Add(Observer observer) { observers.Add(observer); } public void Remove(Observer observer) { observers.Remove(observer); } public string SubjectState { get; set; } public void Notify() { foreach (Observer o in observers) { o.Update(); } } } //抽象观察者 public abstract class Observer { //名字 protected string name; //观察者要知道自己订阅了那个主题 protected ISubject sub; public Observer(string name, ISubject sub) { this.name = name; this.sub = sub; } //接受到通知后的更新方法 public abstract void Update(); } //看股票的同事 public class StockObserver : Observer { public StockObserver(string name, ISubject sub) : base(name, sub) { } public override void Update() { Console.WriteLine($"通知内容:{sub.SubjectState},反应:{name}关闭股票行情,继续工作!"); } } //看NBA的同事 public class NBAObserver : Observer { public NBAObserver(string name, ISubject sub) : base(name, sub) { } public override void Update() { Console.WriteLine($"通知内容:{sub.SubjectState},反应:{name}关闭NBA直播,继续工作!"); } } /// /// 客户端调用 /// class Program { static void Main(string[] args) { Mishu mishu = new Mishu(); //新建同事 观察者角色 Observer tongshi1 = new StockObserver("巴菲特", mishu); Observer tongshi2 = new NBAObserver("麦迪", mishu); //秘书小妹要知道哪些同事要通知(主题要知道所有订阅了自己的观察者) mishu.Add(tongshi1); mishu.Add(tongshi2); //主题状态更改了 mishu.SubjectState = "老板回来了!"; //调用主题的通知方法 mishu.Notify(); Console.ReadKey(); } }
运行程序,执行结果如下:
上边的例子中秘书小妹充当主题角色,上班偷懒的同事充当观察者角色,通过观察者模式实现了功能。但是这里还有一个小问题:上边例子能够成功的前提是所有观察者的角色都有一个Update()方法来执行更新。但是有时候各个观察者并不都是相同的类型,如观察者1收到通知执行Update1()方法,而观察者2收到通知执行的是Update2()方法,这时候采用上边的模式就不能满足需求了。怎么改进呢?①各个观察者不属于同一类,所以不需要抽象观察者类了 ②因为各个观察者的反应不是同一的Update(),所以我们不能foreach遍历观察者集合来统一调用Update()方法了,这时可以考虑通过事件委托在客户端确定所有观察者的反应。改进后的代码如下:
//抽象主题角色 public interface ISubject { //添加观察者 void Add(Observer observer); //删除观察者 void Remove(Observer observer); //主题状态 string SubjectState { get; set; } //通知方法 void Notify(); } //***********************1.定义一个委托 public delegate void EventHandler(); //具体主题角色 秘书类 public class Mishu : ISubject { public event EventHandler Update; //存储要通知的同事 public IListobservers = new List (); public string SubjectState { get; set; } public void Add(Observer observer) { observers.Add(observer); } public void Remove(Observer observer) { observers.Remove(observer); } //**********2.通知方法中不能通过遍历来统一调用每个观察者的Update()方法了,改成执行一个委托 public void Notify() { Update(); } } //抽象观察者角色 public abstract class Observer { protected ISubject sub; protected string name; protected Observer(string name,ISubject sub) { this.name = name; this.sub = sub; } } //具体观察者角色 看股票的同事 public class StockObserver { public string name; public ISubject sub; public StockObserver(string name,ISubject sub) { this.name = name; this.sub = sub; } public void CloseStockMarket() { Console.WriteLine($"通知内容:{sub.SubjectState},反应:{name}关闭股票行情,继续工作!"); } } //具体观察者角色 看NBA的同事 public class NBAObserver { public string name; public ISubject sub; public NBAObserver(string name, ISubject sub) { this.name = name; this.sub = sub; } public void CloseNBA() { Console.WriteLine($"通知内容:{sub.SubjectState},反应:{name}关闭NBA直播,继续工作!"); } } class Program { static void Main(string[] args) { Mishu mishu = new Mishu(); //观察者订阅了主题mishu StockObserver tongshi1 = new StockObserver("巴菲特", mishu); NBAObserver tongshi2 = new NBAObserver("麦迪", mishu); //*******************3.将遍历观察者并调用观察者的Update(),改成了事件委托形式 mishu.Update += tongshi1.CloseStockMarket; mishu.Update += tongshi2.CloseNBA; //主题状态更改,并通知 mishu.SubjectState = "老板回来了!"; mishu.Notify(); Console.ReadKey(); } }
运行结果:
我们看到通过事件委托我们可以实现让不同的观察者调用不同的方法,当我们遇到类似这样的情况:点击一个按钮,有的页面元素显示Show(),有的隐藏Hide(),有的关闭Close()就可以采用这种模式。但是这种模式没有对具体的观察者进行抽象,如果观察者太多也会造成事件委托过于复杂。两种观察者模式的实现各有利弊,我们可以根据实际的情况来选择。
宗上:以上四个模式很经典常用