• 设计模式之创建型设计模式


    单例模式

     单例模式就是整个程序中有且仅有一个实例,该类负责创建自己的对象,并且保证只有一个对象被创建。

     主要有三个步骤:私有化构造函数;创建一个公开的静态方法给外界提供实例;提供一个静态变量重用

    比如:

      public class Singleton
        {
            /// <summary>
            /// 构造函数耗时耗资源
            /// 1 私有化构造函数
            /// </summary>
            private Singleton()//加个参数 防止反射破坏单例,因为反射调用私有构造函数的方法是没法传参数的
            {
                //Activator.CreateInstance(null, true);
                long lResult = 0;
                for (int i = 0; i < 10000000; i++)
                {
                    lResult += i;
                }
                Thread.Sleep(2000);
                Console.WriteLine($"{this.GetType().Name}被构造一次 {Thread.CurrentThread.ManagedThreadId}");
            }
            /// <summary>
            /// 3 提供一个静态变量重用
            /// </summary>
            private static Singleton _Singleton = null; /// <summary>
            /// 2 公开的静态方法提供实例
            /// </summary>
            /// <returns></returns>
            public static Singleton CreateInstance()
            {
                if (_Singleton == null)//是在对象初始化之后,可以并发了
                { 
                  _Singleton = new Singleton(); 
                }
                return _Singleton;
            } 
        } 

    但是现在的代码是不安全的,比如说在多线程下,如果此时有多个线程访问if(_Singleton==null),而且此时实例还没有创建,那么后续就会创建多个实例,就不是单例模式了。

     可以加一个lock:

      private static readonly object Singleton_Lock = new object();
    
            /// <summary>
            /// 2 公开的静态方法提供实例
            /// </summary>
            /// <returns></returns>
            public static Singleton CreateInstance()
            {
                if (_Singleton == null)//不加这个的话,多个线程来的时候都被锁住,就算是多线程那也实现不了并发,和单线程没区别,加了这个判断后面的子线程不走lock,直接返回,可以体现多线程的优点
                {
                    lock (Singleton_Lock)//反多线程--限制并发的
                    {
                        if (_Singleton == null)
                        {
                            _Singleton = new Singleton();
                        }
                    }
                }
                return _Singleton;
            }

    调用:

                   for (int i = 0; i < 1000; i++)
                    {
                        Task.Run(() =>//5个线程并发执行
                        {
                            Singleton singleton = Singleton.CreateInstance();
                            singleton.Show();
                        });
                    }

    这就是双判断锁的经典写法,懒汉式实现(就是说不主动调用创建实例的方法就不会创建这个类的实例对象)。

    下面是通过静态构造函数实现的饿汉式方法:

        public sealed class SingletonSecond
        {
            /// <summary>
            /// 构造函数耗时耗资源
            /// 1 私有化构造函数
            /// </summary>
            private SingletonSecond()
            {
                long lResult = 0;
                for (int i = 0; i < 10000000; i++)
                {
                    lResult += i;
                }
                Thread.Sleep(2000);
                Console.WriteLine($"{this.GetType().Name}被构造一次 {Thread.CurrentThread.ManagedThreadId}");
            }
            /// <summary>
            /// 3 提供一个静态变量重用
            /// </summary>
            private static SingletonSecond _SingletonSecond = null;
            /// <summary>
            /// 静态构造函数:CLR调用,在对象使用前完成初始化且只执行一次
            /// </summary>
            static SingletonSecond()
            {
                _SingletonSecond = new SingletonSecond();
            }
            /// <summary>
            /// 2 公开的静态方法提供实例
            /// </summary>
            /// <returns></returns>
            public static SingletonSecond CreateInstance()
            { 
                return _SingletonSecond;
            }
     
    
        }

    在类中,静态构造函数,静态字段都是先于方法的,普通字段和普通构造方法也是如此,执行类中的方法之前肯定都是要先执行他们的。

     所以,也可以通过静态字段来实现:

        public class SingletonThird
        {
            /// <summary>
            /// 构造函数耗时耗资源
            /// 1 私有化构造函数
            /// </summary>
            private SingletonThird()
            {
                long lResult = 0;
                for (int i = 0; i < 10000000; i++)
                {
                    lResult += i;
                }
                Thread.Sleep(2000);
                Console.WriteLine($"{this.GetType().Name}被构造一次 {Thread.CurrentThread.ManagedThreadId}");
            }
            /// <summary>
            /// 3 提供一个静态变量重用
            /// 静态字段:CLR保障,在使用类型之前完成初始化,且只初始化一次
            /// </summary>
            private static SingletonThird _SingletonThird = new SingletonThird();
            /// <summary>
            /// 2 公开的静态方法提供实例
            /// </summary>
            /// <returns></returns>
            public static SingletonThird CreateInstance()
            {
                return _SingletonThird;
            }  
        }

    下面看这样一段代码:

    public class Singleton
        {
            /// <summary>
            /// 构造函数耗时耗资源
            /// 1 私有化构造函数
            /// </summary>
            private Singleton()//加个参数 防止反射
            {
                //Activator.CreateInstance(null, true);
                long lResult = 0;
                for (int i = 0; i < 10000000; i++)
                {
                    lResult += i;
                }
                Thread.Sleep(2000);
                Console.WriteLine($"{this.GetType().Name}被构造一次 {Thread.CurrentThread.ManagedThreadId}");
            }
            /// <summary>
            /// 3 提供一个静态变量重用
            /// </summary>
            private static Singleton _Singleton = null;
            private static readonly object Singleton_Lock = new object(); 
            /// <summary>
            /// 2 公开的静态方法提供实例
            /// </summary>
            /// <returns></returns>
            public static Singleton CreateInstance()
            {
                if (_Singleton == null)//是在对象初始化之后,可以并发了
                {
                    lock (Singleton_Lock)//反多线程--限制并发的
                    {
                        if (_Singleton == null)
                        {
                            _Singleton = new Singleton();
                        }
                    }
                }
                return _Singleton;
            } 
            //既然是单例,大家用的是同一个对象,用的是同一个方法,那还会并发吗  还有线程安全问题吗?
            public int iTotal = 0;
            public void Show()
            { this.iTotal++; 
            } 
        }

     调用:

                    for (int i = 0; i < 1000; i++)
                    {
                        Task.Run(() =>
                        {
                            Singleton singleton = Singleton.CreateInstance();
                            singleton.Show();
                        });
                    }
                    Thread.Sleep(5000);
                    Singleton singleton = Singleton.CreateInstance();
                    Console.WriteLine(singleton.iTotal); 

     最后输出的结果会是多少呢?答案是不一定。

    因为单例和单线程是没关系的,Show方法没有锁,多线程并发下不一定是多少,在1到1000区间中,包括1和1000。但是不会大于1000或者等于0,单例模式表明了自始至终都只有一个实例,也就是只初始化了一次,就算iTotal不是静态字段,也不会因为多次调用Show方法而被初始化为0,。

    改成下面就是正常了:

          public int iTotal = 0;
            public void Show()
            {
                //加锁
                lock (Singleton_Lock)
                {
                    this.iTotal++;
                }
            }

    为什么要使用单例模式

    首先要知道单例模式不是什么太好的东西,一旦使用了,这个静态对象会一直存储在程序中的。请不要画蛇添足,没有必须单例的,请勿单例。

    如果说在这个进程中只需要一个的,那就可以用单例,比如线程池,数据库连接池,配置文件对象(如果确认维护的数据不会更改的话可以单例)

    原型模式

    跟单例的区别就是新的实例,不是同一个,就不会互相影响。快速复制对象,不走构造函数---浅克隆。

    namespace OOP.DesignerPattern.SingletonPattern
    {
        public class SingletonPrototype
        {
            /// <summary>
            /// 构造函数耗时耗资源
            /// 1 私有化构造函数
            /// </summary>
            private SingletonPrototype()
            {
                long lResult = 0;
                for (int i = 0; i < 10000000; i++)
                {
                    lResult += i;
                }
                Thread.Sleep(2000);
                Console.WriteLine($"{this.GetType().Name}被构造一次 {Thread.CurrentThread.ManagedThreadId}");
            }
            /// <summary>
            /// 3 提供一个静态变量重用
            /// </summary>
            private static SingletonPrototype _SingletonPrototype = new SingletonPrototype();
    
            /// <summary>
            /// 2 公开的静态方法提供实例
            /// </summary>
            /// <returns></returns>
            public static SingletonPrototype CreateInstance()
            {
                SingletonPrototype prototype = (SingletonPrototype)_SingletonPrototype.MemberwiseClone();//内存copy
                //跟单例的区别就是 新的实例 不是同一个,就不会互相影响
                //快速复制对象,不走构造函数---浅克隆
                return prototype;
            }
    
            //既然是单例,大家用的是同一个对象,用的是同一个方法,那还会并发吗  还有线程安全问题吗?
            public int iTotal = 0;
            public void Show()
            { 
                    this.iTotal++; 
            } 
        }
    }

    此时iTotal是多少?

               for (int i = 0; i < 1; i++)
                    {
                        Task.Run(() =>//5个线程并发执行
                        {
                            SingletonPrototype singleton = SingletonPrototype.CreateInstance();
                            singleton.Show();
                        });
                    }
                    Thread.Sleep(1000);
                    SingletonPrototype singleton = SingletonPrototype.CreateInstance();
                    Console.WriteLine(singleton.iTotal); 

    答案是0;因为CreateInstance方法之后copy出了一个新的实例,和之前没有任何关系,所以就是iTotal的默认值0。

     什么时候用?

    比如说构造函数中可能耗时间比较多,那可以不用单例直接用原型模式。

    简单工厂模式

     就是为了转移对象的创建(就是甩锅,把对象的创建转移到专门的类中),就是把对象都放到一起创建。转移了矛盾,但是没有消除矛盾,甚至集中了矛盾。

    看下面的代码:

    namespace FactoryPattern.War3.Interface
    {
        public interface IRace
        {
            /// <summary>
            /// show出王者
            /// </summary>
            void ShowKing();
        }
    }

     下面是游戏中不同种族的信息:

        /// <summary>
        /// War3种族之一
        /// </summary>
        public class Undead : IRace
        {
            public void ShowKing()
            {
                Console.WriteLine("The King of {0} is {1}", this.GetType().Name, "GoStop");
            }
        }
        /// <summary>
        /// War3种族之一
        /// </summary>
        public class ORC : IRace
        {
            public void ShowKing()
            {
                Console.WriteLine("The King of {0} is {1}", this.GetType().Name, "Grubby");
            }
        }
        /// <summary>
        /// War3种族之一
        /// </summary>
        public class NE : IRace
        {
            public void ShowKing()
            {
                Console.WriteLine("The King of {0} is {1}", this.GetType().Name, "Moon");
            }
        }
    
       /// <summary>
        /// War3种族之一
        /// </summary>
        public class Human : IRace
        {
            public Human(int id, DateTime dateTime, string reamrk)
            { }
            public Human()
            { }
    
            public void ShowKing()
            {
                Console.WriteLine("The King of {0} is {1}", this.GetType().Name, "Sky");
            }
        }

     正常情况下调用:

                        IRace iRace = new Human();//面向抽象
                        iRace.ShowKing(); 

     但是如果使用简单抽象工厂模式,将对象的创建都集中起来,通过设置一些枚举类型(其它符合业务的方式也都可以)来创建不同的对象:

        /// <summary>
        /// 简单工厂转移矛盾,但是没有消除矛盾
        /// 而且还集中了矛盾  
        /// </summary>
        public class SimpleFactory
        {
            public static IRace CreateInstance(RaceType raceType)
            {
                IRace iRace = null;
                switch (raceType)
                {
                    case RaceType.Human:
                        iRace = new Human();
                        break;
                    case RaceType.Undead:
                        iRace = new Undead();
                        break;
                    case RaceType.NE:
                        iRace = new NE();
                        break;
                    case RaceType.ORC:
                        iRace = new ORC();
                        break;
                    default:
                        throw new Exception("wrong raceType");
                }
                return iRace;
            }  
            public enum RaceType
            {
                Human,
                Undead,
                NE,
                ORC
            }
        }

     调用:

                    {
                        IRace iRace = SimpleFactory.CreateInstance(SimpleFactory.RaceType.Human);// new Human();//怎么样更面向抽象
                        iRace.ShowKing();
                    }

     就是很简单,简单到23种设计模式中都没有它,就是转移了矛盾,集中了起来。但是必须要说的是,简单工厂模式的确比之前的写法要高级,甚至减少了耦合,比如之前写IRace iRace=new Hunman(),这是直接和Human这个类耦合了万一哪天Human类中没有无参构造函数了,那这个写法就报错了,就需要到调用类中来改代码,需要改动业务逻辑这里,使用简单工厂模式之后,就把矛盾问题转移到了工厂类中,不改动业务逻辑的类,减少了风险,降低了耦合。

     当然,现在是需要传入枚举信息然后返回对象的,但是我们也可以通过配置文件进行配置,根据实际业务来选择:

      private static string IRaceConfigString = System.Configuration.ConfigurationManager.AppSettings["IRaceConfig"];
            /// <summary>
            /// 可配置
            /// </summary>
            /// <returns></returns>
            public static IRace CreateInstanceConfig()
            {
                RaceType raceType = (RaceType)Enum.Parse(typeof(RaceType), IRaceConfigString);
                return CreateInstance(raceType);
            }

    配置文件:

     <appSettings>
        <add key="IRaceConfig" value="Human"/> 
      </appSettings>

     调用:

             {
               //可配置
               IRace iRace = SimpleFactory.CreateInstanceConfig();
               iRace.ShowKing();
             }

     如果你不想new,但是又想获取对象实例,可以通过反射来实现:

             //如果你不想new,但是又想获取对象实例,有哪几种方法? 
            private static string IRaceConfigReflectionString = System.Configuration.ConfigurationManager.AppSettings["IRaceConfigReflection"];
            public static IRace CreateInstanceConfigReflection()
            {
                Assembly assembly = Assembly.Load(IRaceConfigReflectionString.Split(',')[1]);
                Type type = assembly.GetType(IRaceConfigReflectionString.Split(',')[0]);
                return (IRace)Activator.CreateInstance(type);
            }

    配置文件中:

      <appSettings> 
        <add key="IRaceConfigReflection" value="FactoryPattern.War3.Service.Human,FactoryPattern.War3.Service"/>
      </appSettings>

    工厂方法模式(工厂模式)

     上面简单工厂将矛盾转移,集中了矛盾,但是没有消除矛盾(没有使用反射的前提下)。所以我们可以通过工厂方法模式来消除集中的矛盾,工厂方法模式能够屏蔽细节,进行扩展。

     我们给游戏中每一个种族都创建一个工厂类,具体的对象创建细节都在对应的种族工厂中实现,上层只是调用获得对应的对象信息,至于怎么创建,创建过程中需要哪些信息,都交给工厂来处理。比如我们需要一个人族,那就调用人族工厂开放出来的接口,具体的实现交给工厂解决,细节不开放给上层。

     例如:

      public interface IFactory
        {
            IRace CreateInstance();
        }
        public class HumanFactory : IFactory
        {
            public virtual IRace CreateInstance()
            {
                //IRace iRace = new Human();
                IRace iRace = new Human(123, DateTime.Now, "123");
                return iRace;
            }
        }
        public class UndeadFactory : IFactory
        {
            public IRace CreateInstance()
            {
                IRace iRace = new Undead();
                return iRace;
            }
        }

     调用:

                    {
                        IFactory factory = new HumanFactory();
                        //就是为了扩展(mvc扩展IOC就知道了)  为了屏蔽细节
                        IRace race = factory.CreateInstance();
                    }

    比如说后期制造不同种族的工艺可能有需要改进的地方,那就在子类工厂中实现,旧工艺可能也还会有用到的地方,所以留着,只需要在逻辑层(上层)调用的时候做更改就可以。这就是证明了工厂方法模式也有着扩展能力的一方面。

    namespace OOP.DesignerPattern.ThreeFactory.FactoryMethod
    {
        public class HumanFactory : IFactory
        {
            /// <summary>
            /// 实现接口,并且设置为虚方法,继承类可重写
            /// </summary>
            /// <returns></returns>
            public virtual IRace CreateInstance()
            {
                //IRace iRace = new Human();
                IRace iRace = new Human(123, DateTime.Now, "123");
                return iRace;
            }
        }
        /// <summary>
        /// 继承父类并重写,后期可能有需要改进的地方,那就在子类工厂中实现,旧工艺可能也还会有用到的地方,所以留着,只需要在逻辑层(上层)调用的时候做更改就可以。
        /// </summary>
        public class HumanFactoryChild : HumanFactory
        {
            public override IRace CreateInstance()
            {
                Console.WriteLine("12345667..");
                //IRace iRace = new Human();
                IRace iRace = new Human(123, DateTime.Now, "123");
                return iRace;
            }
        }
    }

     调用:

                      {
                        IFactory factory = new HumanFactory();
                        //就是为了扩展(mvc扩展IOC就知道了)  为了屏蔽细节
                        IRace race = factory.CreateInstance();
    
                        IFactory factory1 = new HumanFactoryChild();
                        IRace race1 = factory1.CreateInstance();
                    }

     抽象工厂模式

     在工厂方法模式的基础加了一个抽象的约束(一定要注意,抽象方法是没有实现主体的,所以必须要在继承类中实现,不然编译不过去,所以在这里加了继承抽象类的约束,就必须要实现基类中的所有抽象方法,虚方法就不这样)。需要明确的一点是这样的话最好不要去扩展产品簇( 抽象工厂类中定好的对象)在这里可以理解为尽量不要扩展抽象工厂类中定好的对象,一旦增加,对应的继承类也都要增加。

    比如说,我们生成一个种族,这个种族会有很多能力,比如 使用武器,采集资源等,那我们就放到一个工厂中进行处理:

    抽象约束:

      public abstract class AbstractFactoryBase
        {
            public abstract IRace CreatRace();
            public abstract IArmy CreateArmy();
    
            public abstract IResource CreateResource();
        }

     实现工厂类:

        public class UndeadFactoryAbstract : AbstractFactoryBase
        {
            public override IRace CreatRace()
            {
                return new Undead();
            }
            public override IArmy CreateArmy()
            {
                return new UndeadArmy();
            }
            public override IResource CreateResource()
            {
                return new UndeadResource();
            }
        }
     
        public class HumanFactoryAbstract : AbstractFactoryBase
        {
            public override IRace CreatRace()
            {
                return new Human();
            }
            public override IArmy CreateArmy()
            {
                return new HumanArmy();
            }
            public override IResource CreateResource()
            {
                return new HumanResource();
            } 
        }

    调用:

                      {
                        //工厂方法+ 抽象--是必须全部实现的:方便扩展种族 但是不能扩展产品簇--倾斜性可扩展性设计
                        AbstractFactoryBase factory = new HumanFactoryAbstract();
                        IRace race = factory.CreatRace();
                        IArmy army = factory.CreateArmy(); 
                        IResource resource = factory.CreateResource();
                       }

    创建型设计模式的核心套路,就是管理对象创建 

    工厂模式详解

  • 相关阅读:
    【Struts2】 国际化
    【Struts2】进阶
    【Struts2】简介及入门
    【Distributed】互联网安全架构
    【Mac】 开启原生的 NTFS 硬盘格式支持
    swagger2简单使用
    自定义异常和异常处理
    enum简单使用
    Interceptor
    修改docker下mysql配置
  • 原文地址:https://www.cnblogs.com/anjingdian/p/15368097.html
Copyright © 2020-2023  润新知