创建型

Demo链接:https://files.cnblogs.com/files/summerZoo/CreaterPattern.zip?t=1651912610

概述:

  1. 创建型模式对类的实例化过程进行了抽象
  2. 将软件模块中对象的创建和对象的使用分离
  3. 暴露创建接口,屏蔽创建细节,让关于自身的逻辑内聚在内部,符合单一职责原则

要素

  • 创建什么(What)
  • 由谁创建(Who)
  • 何时创建(when)

创建型模式主要从这三个方面考虑,为程序设计提供更大的灵活性。

创建型模式隐藏了类的实例的创建细节,通过隐藏对象如何被创建和组合在一起达到使整个系统独立的目的。

简单工厂模式(Simple Factory)

定义

  • 又称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。
  • 在简单工厂模式中,可以根据参数的不同返回不同类的实例。
  • 简单工厂模式专门定义一个工厂类负责创建其他类的实例,被创建的实例通常都具有共同的父类

最大优点:将对象的创建与对象的使用分离

最大缺点:不够灵活,有新的具体产品新增或增加时,需要修改原有逻辑代码

结构

  • 工厂角色——负责实现创建所有实例的内部逻辑;
  • 抽象产品角色——所创建的所有对象的父类,负责描述所有实例所共有的公共接口;
  • 具体产品角色——创建目标,所有创建的对象都充当这个角色的某个具体类的实例。

实例

Demo1

场景1:

  1. 一个代工厂可以提供多个品牌电视的生产线(如海尔、TCL等)
  2. 调用者(Prgram)不需要知道这些生产出的电视的名称,只需要知道表示该电视类的一个品牌参数及创建方法
  3. 把该参数传入方法即可返回一个相应的按钮对象

公共父类

   public interface TV
    {
         //播放
         void play();
    }

子类

    public class HairTV : TV
    {
        public void play()
        {
            Console.WriteLine("海尔电视正在播。。。。"); 
        }
    }
    public class TCLTV : TV
    {
        public void play()
        {
            Console.WriteLine("TCLTV电视正在播。。。。"); 
        }
    }

工厂:

  public  class TVFactoty
    {
        public static TV GetTV(string Brand)
        {
            if (Brand.Equals("Hair"))
            {
                Console.WriteLine("生产一台海尔电视");
                return new HairTV();
            }else if (Brand.Equals("TCL"))
            {
                Console.WriteLine("生产一台TCL电视");
                return new HairTV();
            }
            else
            {
                throw new Exception($"没有对应{Brand}品牌的生产线");
            }
        }
    }

调用:

TV tv = TVFactoty.GetTV("Hair");
tv.play();

Demo2

权限管理
在某OA系统中,系统根据对比用户在登录时输入的账号和密码以及在数据库中存储的账号和密码是否一致来进行身份验证,

如果验证通过,则取出存储在数据库中的用户权限等级(以整数形式存储),根据不同的权限等级创建不同等级的用户对象,不同等级的用户对象拥有不同的操作权限。

现使用简单工厂模式来设计该权限管理模块

https://cdn.jsdelivr.net/gh/summerZoo123/image-hosting@master/image.2hpfsie3tvc0.webp

父类:

   public abstract class User
    {
        //不同角色的公共方法
        public virtual void sameOperation()
        {
            Console.WriteLine("修改个人休息");
        }
        //子类不同的操作权限的方法
        public abstract void diffOperation();
    }

子类:

    public class Employee : User
    {
        public override void diffOperation()
        {
            Console.WriteLine("雇员只有请假权限"); ;
        }
    }
    public class Manager : User
    {
        public override void diffOperation()
        {
            Console.WriteLine("主管有请假,审批"); 
        }
    }
    public class Administrator : User
    {
        public override void diffOperation()
        {
            Console.WriteLine("管理员拥有创建和管理假条权限"); 
        }
    }

身份验证类:

   public class IdentifyHelper
    {
        public static int IdentifyPermission(string name,string pwd)
        {
            if (name.Equals("zs")&& pwd.Equals("123"))
            {
                return 1;
            }else if (name.Equals("rookie") && pwd.Equals("456"))
            {
                return 2;
            }
            else
            {
                return 0;
            }
        }
    }

工厂类:

   public  class UserFactory
    {
		public static User getUser(int permission)
		{
			if (0 == permission)
			{
				return new Employee();
			}
			else if (1 == permission)
			{
				return new Manager();
			}
			else if (2 == permission)
			{
				return new Administrator();
			}
			else
			{
				return null;
			}
		}
	}

调用:

 User user;
 int level = IdentifyHelper.IdentifyPermission("zs", "123");
 user = UserFactory.getUser(level);
 user.sameOperation();
 user.diffOperation();

结果:

修改个人休息
主管有请假,审批

优缺点

缺点

  • 工厂类集中了所有产品创建逻辑,又涉及经常改动,系统风险变高
  • 工厂里的对象创建原来是在类里,工程类新增了类的个数,增加了系统复杂度
  • 系统拓展困难,一旦有新的产品,就得增加ifelse的逻辑,产品过多后,逻辑就会过于复杂不利于拓展和维护
  • 采用了静态工厂,造成了工厂角色无法形成继承的等级的结构

优点

  • 实现了对责任的分割,它提供了专门的工厂类用于创建对象。
  • 对客户端屏蔽了产品类的创建细节,提供了只需少量参数的创建接口
  • 可以引入配置文件,在不修改客户端代码的情况下更换或增加新的具体产品类

适用场景

  • 工厂类里的产品类型比较少
  • 客户端不关注创建对象细节,只想简化获取对象的过程(采用尽可能少的参数)

工厂方法模式(Factory Method)

前情提要:

简单工厂模式有以下缺点

  • 所有产品类的创建都封装在一个工厂类,产品类与工厂类的耦合度高,影响了系统灵活性和拓展性
  • 有新的产品类型加入时,需要修改原有的类型创建逻辑,违背了”开放—封闭原则“

思考:我们能不能将产品类的创建不封装到一个工厂类,只要产品类的创建分散到不同的工厂类,有新的产品类型时,我们可以创建对应新的工厂类,不用改变原有类的工厂类。变修改为新增,更符合”开放—封闭原则“

模式结构

定义

  • 又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式、多态工厂(Polymorphic Factory)模式
  • 工厂方法模式是简单工厂模式的进一步抽象和推广,使用了面向对象的多态性,保持了简单工厂模式的优点,而且克服了它的缺点
  • 将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类
  • 工厂方法模式可以允许系统在不修改原有工厂角色的情况下引进新产品

结构

  • 抽象工厂角色——负责定义创建产品对象的公共接口;
  • 具体工厂角色——负责实现生成具体的产品对象的方法
  • 抽象产品角色——所创建的所有对象的父类,负责描述所有实例所共有的公共接口;
  • 具体产品角色——创建目标,所有创建的对象都充当这个角色的某个具体类的实例。

实例

Demo1

抽象产品类

   public interface TV
    {
         void play();
    }

产品类:

    public class HairTV : TV
    {
        public void play()
        {
            Console.WriteLine("海尔电视正在播。。。。"); 
        }
    }
    public class TCLTV : TV
    {
        public void play()
        {
            Console.WriteLine("TCLTV电视正在播。。。。"); 
        }
    }

抽象工厂类

  public interface  TVFactoty
  {
        public TV GetTV();
  }

工厂类

    public class HairTVFactory: TVFactoty
    {
        public TV GetTV()
        {
            Console.WriteLine("海尔电视生产一台");
            return new HairTV();
        }
    }
    public class TCLTVFactory : TVFactoty
    {
        public TV GetTV()
        {
            Console.WriteLine("TCL电视生产一台");
            return new TCLTV();
        }
    }

测试

            TV tv1 = new HairTVFactory().GetTV();
            tv1.play();
            TV tv2 = new TCLTVFactory().GetTV();
            tv2.play();

结果打印

海尔电视生产一台
海尔电视正在播。。。。
TCL电视生产一台
TCLTV电视正在播。。。。

Demo2

( 4、工厂方法模式及扩展)

优缺点

优点

  • 用户只需要关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名(因为指定了创建的具体工厂,不用通过传参去确定产品类型)。
  • 工厂可以自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部
  • 系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品;只要添加一个具体工厂和具体产品就可以了,更符合”开放——封闭原则“

缺点

  • 有新的产品类时,需要创建与之对应的新的具体工厂类。系统的中类的个数成对增加,增加了系统的复杂度
  • 引入了抽象工厂的概念,增加了系统的抽象性和理解难度
  • 在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。

适用情况

  • 一个类不知道它所需要的对象的类;
  • 一个类通过其子类来指定创建哪个对象;
  • 将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定。

模式扩展

  • 使用多个工厂方法:在抽象工厂角色中可以定义多个工厂方法,从而使具体工厂角色实现这些不同的工厂方法,这些方法可以包含不同的业务逻辑,以满足对不同的产品对象的需求。
  • 产品对象的重复使用:工厂对象将已经创建过的产品保存到一个集合(如数组、List等)中,然后根据客户对产品的请求,对集合进行查询。如果有满足要求的产品对象,就直接将该产品返回客户端;如果集合中没有这样的产品对象,那么就创建一个新的满足要求的产品对象,然后将这个对象在增加到集合中,再返回给客户端。
  • 多态性的丧失和模式的退化:如果工厂仅仅返回一个具体产品对象,便违背了工厂方法的用意,发生退化,此时就不再是工厂方法模式了。一般来说,工厂对象应当有一个抽象的父类型,如果工厂等级结构中只有一个具体工厂类的话,抽象工厂就可以省略,也将发生了退化。当只有一个具体工厂,在具体工厂中可以创建所有的产品对象,并且工厂方法设计为静态方法时,工厂方法模式就退化成简单工厂模式。

抽象工厂模式(Abstract Factory)

背景:在工厂方法模式里已经对简易工厂模式进行了升级,但是同样引入了一个新的问题,新创建一个产品就需要引入一个具体的工厂类,这个工厂类里只有一个创建该产品的方法。这引发了下面的思考

  • 一个工厂里只生产一种产品吗?这是不是很大的浪费
  • 能不能做到这个工厂还能生产多个其他产品呢?

基础概念(产品等级结构、产品族)

  • 产品等级结构:产品等级结构即产品的继承结构。(电视——海尔电视、冰箱——海尔冰箱)
  • 产品族:产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品。(海尔电视,海尔冰箱,海尔洗衣机)

具体例子

  • Button、Text是两个产品结构

    https://cdn.jsdelivr.net/gh/summerZoo123/image-hosting@master/image.64hsgduabmg0.webp

  • Linux、Windows、Unix是不同产品族

动机

  • 多个产品,位于不同产品等级结构中,不同产品族中
  • 在原有工厂方法基础再次抽象,扩大了产品的种类范围

与工厂方法模式的区分

  • 工厂方法——一个产品族,多个等级结构的产品
  • 抽象工厂方法——多个产品族,多个等级结构的产品(在产品种类上有拓展)

结构

  • 抽象工厂——用于声明生成各个产品等级结构的抽象产品(一个产品族里的抽象产品)的创建方法的接口
  • 具体工厂——实现了抽象工厂声明的生成抽象产品的方法,这些产品构成了一个产品族
  • 抽象产品——每种产品声明接口,在抽象产品中定义了产品的抽象业务方法
  • 具体产品——具体工厂生产的具体产品对象,实现抽象产品接口中定义的业务方法。

实例

抽象产品

   public interface TV
    {
         void play();
    }
    public interface Fridge
    {
        void CreateIce();
    }

具体产品

//"海尔"族类产品
    public class HairTV : TV
    {
        public void play()
        {
            Console.WriteLine("海尔电视正在播。。。。"); 
        }
    }
        public class HairFridge : Fridge
    {
        public void CreateIce()
        {
            Console.WriteLine("海尔冰箱开始制冰"); ;
        }
    }
//"TCL"族类产品
    public class TCLTV : TV
    {
        public void play()
        {
            Console.WriteLine("TCLTV电视正在播。。。。"); 
        }
    }
    public class TCLFridge : Fridge
    {
        public void CreateIce()
        {
            Console.WriteLine("TCL冰箱开始制冰"); 
        }
    }

抽象工厂

  public  interface Factoty
  {
        //生产电视
        public  TV GetTV() ;
        //生产冰箱
        public Fridge GetFridge();
  }

优缺点

优点
  • 可以通过具体工厂类创建一个产品族中的多个对象,增加或者替换产品族比较方便,增加新的具体工厂和产品族很方便;
缺点
  • 增加新的产品等级结构很复杂,需要修改抽象工厂所有的具体工厂类,对“开闭原则”支持性降低

单例模式(Singleton)

背景

对于系统中的某些类,我们希望一次创建。让它常驻在内存里,我们后续使用时调用,避免重复创建对象的操作,思路排除:

  • 定义一个全局变量可以确保对象随时都可以被访问,但不能防止我们实例化多个对象(不行)
  • 让类自身负责保存它的唯一实例(可行),这个类可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法。这就是单例模式的模式动机。

定义

  • 单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。

规则

  • 一个类只有一个实例
  • 这个类必须自行创建这个实例
  • 类必须自行向整个系统提供这个实例的获取方法

结构

单例类,内部成员

  • 静态私有成员变量
  • 私有构造函数,确保外部无法通过new关键字去创建实例
  • 静态公有的工厂方法,方法需要以下几个功能
    1. 检验实例是否为空,为空则创建实例,存储在静态私有变量里(确保唯一)
    2. 返回实例化对象

常见模式

  • 饿汉式单例——在自己被加载时就将自己实例化

    单从资源利用效率角度来讲,这个比懒汉式单例类稍差些。从速度和反应时间角度来讲,则比懒汉式单例类稍好些。

  • 懒汉式单例——在被调用时,按需生成或返回实例

    必须处理好在多个线程同时首次引用此类时的访问限制问题我想跳转

实例

单线程饿汉模式
    public class FullPatternSingleton
    {
        //静态类变量只会实例化一次
        private static FullPatternSingleton instance = new FullPatternSingleton();
        private FullPatternSingleton()
        {

        }
        public static FullPatternSingleton GetInstance()
        {
            return instance;
        }
        
    }
单线程饿汉模式(利用静态构造方法创建实例)
    public class FullPatternSingleton2
    {
        private static FullPatternSingleton2 singleton2 = null;
        /// <summary>
        /// 构造函数
        /// </summary>
        private FullPatternSingleton2()
        {
          
        }

        /// <summary>
        /// 采用静态构造函数,在类被加载时就会被执行,且仅仅执行一次,所以可以用于构造实例的
        /// </summary>
        static FullPatternSingleton2()
        {
            singleton2 = new FullPatternSingleton2();
        }
        public static FullPatternSingleton2 GetInstance()
        {
            return singleton2;
        }
    }
单线程懒汉模式
 public class HungrySingletonPattern
    {
        //静态私有变量
        private static HungrySingletonPattern  singletonInstance= null;
        //私有构造方法
        private  HungrySingletonPattern()
        {
            
        }
        //暴露给外界获取实例的方法
        public static HungrySingletonPattern GetInstance()
        {
            if (singletonInstance == null)
            {
                singletonInstance = new HungrySingletonPattern();
            }
            return singletonInstance;
        }
        public void dosomething()
        {
            Console.WriteLine("单例类的public方法");
        }
    }
多线程饿汉模式
    public class SingletonInMultiThread
    {
        private static SingletonInMultiThread instance = new SingletonInMultiThread();
        public string Name { get; set; }
        private SingletonInMultiThread()
        {
            //this.Name = name;
        }
        public static Object ObjLock = new object();
        public static SingletonInMultiThread GetInstance() {
            return instance;
            //lock (ObjLock)
            //{
            //    return instance;
            //}
            
        }
    }
多线程懒汉模式
   public class SingletonInMultiThread2
    {
        private static SingletonInMultiThread2 instance ;
        private SingletonInMultiThread2()
        {
            
        }
        public static Object ObjLock = new object();
        public static SingletonInMultiThread2 GetInstance() {
            if (instance == null)
            {
                //kkk
                lock (ObjLock)
                {
                    //这里还要加一层判断,因为在多线程并发的情况下,会出现以下场景:
                    //多个线程可能都执行到kkk的位置,然后在kkk位置排队等待锁释放
                    //获取锁的第一个线程会创建一个实例后释放锁
                    //等待在kkk位置的第二个线程,拿到锁,又会创建一个新的实例
                    //所以要规避这种情况,需要在锁的内部创建实例时再加一层实例是否为空的判断
                    if (instance == null)
                    {
                        instance = new SingletonInMultiThread2();
                    }
                    //有同学好奇,为什么要加两层实例为空的判断
                    //其实最外层实例是否为空是基于性能考虑, 如果判断不为空,就不会进入排队等待锁的逻辑;
                    //如果不加这层判断,每个线程都会参与锁的竞争,对程序性能不力,但是仍可以保证只产生一个实例
                    //第二层在锁的内部做的实例是否为空的判断是必要的,不加无法保证唯一实例

                }
            }
            return instance;
        }
    }

优缺点

  • 优点——提供了对唯一实例的受控访问并可以节约系统资源
  • 缺点——因为缺少抽象层而难以扩展,且单例类职责过重。

适用于系统只需要一个实例对象;客户调用类的单个实例只允许使用一个公共访问点

建造者模式

背景

  • 现实世界中还是在软件系统中,都存在一些复杂的对象,它们拥有多个组成部分(比如计算机:由主板、cpu、显卡、电源等组成)
  • 对于用户而言,他们不需要了解产品的组装细节,只需要一台完整的组装好的产品
  • 在软件开发中,也存在大量类似汽车一样的复杂对象,它们拥有一系列成员属性,这些成员属性中有些是引用类型的成员对象
  • 复杂对象相当于一辆有待建造的汽车,而对象的属性相当于汽车的部件,建造产品的过程就相当于组合部件的过程。由于组合部件的过程很复杂,因此,这些部件的组合过程往往被“内聚化”到一个称作建造者的对象里,建造者返还给客户端的是一个已经建造完毕的完整产品对象,而用户无须关心该对象所包含的属性以及它们的组装方式

建造者模式可以将部件和其组装过程分开,在对客户端屏蔽情况下一步一步创建对象并且返回

给用户。

定义

  • 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
  • 建造者模式是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们

模式结构

Builder——抽象建造者(概念生产工厂,定义产品的创建方法和返回方法)
ConcreteBuilder——具体建造者(实际生产工厂)
Director——指挥者(制定生产工序,先做零件A,再做零件B,再组装。。。。):一方面它隔离了客户与生产过程;另一方面它负责控制产品的生成过程
Product——产品角色(产品)

Demo

产品Product定义:定义一个套餐类Meal,套餐由主食和饮料组成

   public class Meal
    {
        //假设套餐由主食和饮料组成
        public string drink { get; set; }
        public string food { get; set; }
    }

抽象建造者Builder

   //套餐创建抽象类
    public abstract class MealBuilder
    {
        protected Meal meal = new Meal();
        public abstract void setFood();
        public abstract void setDrink();

        public Meal GetMeal()
        {
            return meal;
        }

    }

定义两个具体的建造者,KFCBuilder(肯德基店)、MacDonaldBuilder(麦当劳店),模拟每个店的套餐不一样

    //KFC
    public class KFCBuilder : MealBuilder
    {
        public override void setFood()
        {
            //这里可以放食物的创建过程
            meal.food = "麦辣鸡腿堡";
        }

        public override void setDrink()
        {
            //这里可以放饮料的详细制作过程
            meal.drink = "百事可乐";
        }
    }
    //MacDonald
    public class MacDonaldBuilder : MealBuilder
    {
        public override void setFood()
        {
            //这里可以放食物的创建过程
            meal.food = "巨无霸双层堡";
        }

        public override void setDrink()
        {
            //这里可以放饮料的详细制作过程
            meal.drink = "可口可乐";
        }
    }

定义一个指挥者,在例子里就是店员角色,她负责套餐的制作及为用户提供做好的套餐

    public class Waiter
    {
        private MealBuilder _MealBuilder;
        public Waiter(MealBuilder mealBuilder)
        {
            this._MealBuilder = mealBuilder;
        }
        public  Meal GetMeal()
        {
            _MealBuilder.setFood();
            _MealBuilder.setDrink();
            return _MealBuilder.GetMeal();
        }
    }

实际用户调用:客户无需关心生产细节,只需确定具体建造者的类型即可

            //用户指定店铺,这里用户选的是肯德基的店
            MealBuilder builder = new KFCBuilder();
            //用户找的店员自然是肯德基的店员
            Waiter waiter = new Waiter(builder);
            //用户只需要从店员那里取餐就行了,他不需要知道套餐是咋做的
            Meal meal = waiter.GetMeal();
            Console.WriteLine($"当前套餐的食物是{meal.food},饮料是{meal.drink}");

输出:当前套餐的食物是麦辣鸡腿堡,饮料是百事可乐

优缺点

优点
  • 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
  • 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象
  • 可以更加精细地控制产品的创建过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
  • 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合“开闭原则”。
缺点
  • 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
  • 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。

适用场景

  • 产品对象通常包含多个成员属性,有复杂的内部结构。但是属性比较固定
  • 需要生成的产品对象的属性相互依赖,需要指定其生成顺序
  • 对象的创建过程在不在对象所在类中定义,定义在指挥者类里。
  • 隔离复杂对象的创建和使用,并使得相同的创建过程可以通过不同的创建对象创建不同的产品。

常见例子

  1. 邮件的构建(发件人、接收人、抄送人、标题、内容、附件)
  2. 游戏人物装扮

建造者模式的简化

  • 省略抽象建造者角色:如果系统中只需要一个具体建造者的话,可以省略掉抽象建造者。
  • 省略指挥者角色:在具体建造者只有一个的情况下,如果抽象建造者角色已经被省略掉,那么还可以省略指挥者角色,让Builder角色扮演指挥者与建造者双重角色。

建造者模式与抽象工厂模式的比较

  • 建造者模式返回一个组装好的完整产品

    抽象工厂模式返回一系列相关的产品,这些产品位于不同的产品等级结构,构成了一个产品族。

  • 在抽象工厂模式中,客户端实例化工厂类,然后调用工厂方法获取所需产品对象,

    建造者模式中,客户端可以不直接调用建造者的相关方法,而是通过指挥者类来指导如何生成对象,包括对象的组装过程和建造步骤,它侧重于一步步构造一个复杂对象,返回一个完整的对象。

  • 如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品族的产品(海信电视、TCL电视)

    那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车

原型模式(Prototype Pattern)

C# 设计模式-原型模式 - shine声 - 博客园 (cnblogs.com)

动机

在软件系统中,有些对象的创建过程较为复杂,而且有时候需要频繁创建。

使用原型模式可以快速复制一个对象自身,克隆出多个与原型对象一模一样的对象。

定义

原型模式——用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。

可以直接复制一个现有的对象,而不需要重新new操作去创建。这种创建分两种方式,浅拷贝和深拷贝

模式结构

Prototype——抽象原型类
ConcretePrototype——具体原型类
Client——客户类

模式创建

  • ICloneable是系统自带的接口,来定义需要用clone方法的。
  • 只要实现ICloneable接口,都可以通过重写clone方法去实现复制方法
    public class Person:ICloneable
    {
        public int Age { get; set; }
        public string Name { get; set; }
        
        public object Clone()
        {
           //浅拷贝
           return this.MemberwiseClone();
        }
    }
浅拷贝
this.MemberwiseClone();
深拷贝
  • 序列化和反序列化创建了一个新的对象,这样拷贝出来的类指向就不会和原有的类一样。
  • 创建一个
    /// <summary>
    /// 深拷贝1(通过序列化)
    /// </summary>
    /// <returns></returns>
    public object Clone()
    {
        XmlSerializer serializer = new XmlSerializer(typeof(Person));
        using (var stream = new System.IO.MemoryStream())
        {
            serializer.Serialize(stream, this);
            stream.Seek(0, System.IO.SeekOrigin.Begin);
            return serializer.Deserialize(stream) as Person;
        }
    }
        /// <summary>
        /// 通过转化成json序列化
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <returns></returns>
        public static T CloneJson<T>(this T source)
        {
            if (Object.ReferenceEquals(source, null))
            {
                return default(T);
            }

            var deserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace };
            return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source), deserializeSettings);
        }

优缺点

优点:

  1.隐藏了创建实例的繁琐过程,只需要通过clone方法就能获取实例。

  2.使用拷贝代替new,减少资源损耗。

缺点:

  需要在每个需要拷贝的类实现中实现clone方法。