zoukankan      html  css  js  c++  java
  • 设计模式之创建型设计模式

    单例模式

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

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

    比如:

      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();
                       }

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

    工厂模式详解

  • 相关阅读:
    教程:在 Visual Studio 中开始使用 Flask Web 框架
    教程:Visual Studio 中的 Django Web 框架入门
    vs2017下发现解决python运行出现‘No module named "XXX""的解决办法
    《sqlite权威指南》读书笔记 (一)
    SQL Server手工插入标识列
    hdu 3729 I'm Telling the Truth 二分图匹配
    HDU 3065 AC自动机 裸题
    hdu 3720 Arranging Your Team 枚举
    virtualbox 虚拟3台虚拟机搭建hadoop集群
    sqlserver 数据行统计,秒查语句
  • 原文地址:https://www.cnblogs.com/anjingdian/p/15368097.html
Copyright © 2011-2022 走看看