天天看點

面向對象23種設計模式系列(一)- 建立型設計模式

本系列将和大家分享面向對象23種設計模式中常用的幾種設計模式,本章主要簡單介紹下建立型設計模式。

本章是面向對象23種設計模式系列開篇,首先我們來看下什麼是設計模式?

面向對象23種設計模式:

  1、面向對象語言開發過程中,遇到的種種場景和問題,提出了解決方案和思路,沉澱下來就變成了設計模式。

  2、解決具體問題的具體招數---套路---站在前輩的肩膀上。

  3、沒有什麼設計模式是完美無缺的,一種設計模式就是解決一類問題,通常設計模式在解決一類問題的同時,還會帶來别的問題,我們設計者要做的事兒,就是要揚長避短,充分發揮長處!

設計模式可以大概分為三大類:

  1、建立型設計模式:關注對象的建立。

  2、結構型設計模式:關注類與類之間的關系。

  3、行為型設計模式:關注對象和行為的分離。

我們要做的就是學習核心套路,這裡就不做過多的描述,如果有機會會通過具體例子再和大家分享。下面我們正式進入本章主題。

建立型設計模式:關注對象的建立。(5個)

1、單例模式(Singleton Pattern)

單例模式:

  就是限制了對象的建立,重用了對象。保證程序中,某個類隻有一個執行個體。

  即使是單例,變量也不是線程安全的,單例不是為了保證線程安全。

  單例的好處就是單例,就是全局唯一的一個執行個體。

  應對一些特殊情況,比如資料庫連接配接池(内置了資源) ,全局唯一号碼生成器。

  單例可以避免重複建立,但是也會常駐記憶體,除非是真的有必要,否則就不要使用單例。

1.1、單例模式經典寫法(懶漢式)

using System;
using System.Threading;

namespace SingletonPattern
{
    /// <summary>
    /// 懶漢式單例模式(經典寫法)
    /// 單例類:一個構造對象很耗時耗資源類型。
    /// </summary>
    public class Singleton
    {
        /// <summary>
        /// 構造函數耗時耗資源
        /// </summary>
        private Singleton()
        {
            long lResult = 0;
            for (int i = 0; i < 10000000; i++)
            {
                lResult += i;
            }
            Thread.Sleep(2000);
            Console.WriteLine("{0}被構造一次", this.GetType().Name);
        }

        /// <summary>
        /// 全局唯一靜态  重用這個變量
        /// volatile 促進線程安全 讓線程按順序操作
        /// </summary>
        private static volatile Singleton _singleton = null;

        /// <summary>
        /// 引用類型對象
        /// </summary>
        private static readonly object lockSingleton = new object();

        /// <summary>
        /// 公開的靜态方法提供對象執行個體
        /// </summary>
        /// <returns>
        /// </returns>
        public static Singleton CreateInstance()
        {
            if (_singleton == null) //_singleton已經被初始化之後,就不要進入鎖等待了
            {
                //保證任意時刻隻有一個線程進入lock範圍
                //也限制了并發,尤其是_singleton已經被初始化之後,故使用了雙if來解決并發限制問題
                lock (lockSingleton)
                {
                    //Thread.Sleep(1000);
                    //Console.WriteLine("等待鎖1s之後才繼續。。。");
                    if (_singleton == null) //保證隻執行個體化一次
                    {
                        _singleton = new Singleton();
                    }
                }
            }

            return _singleton;
        }

        public int iTotal = 0;

        /// <summary>
        /// 既然是單例,大家用的是同一個對象,用的是同一個方法,那還會并發嗎  還有線程安全問題嗎?
        /// 即使是單例,變量也不是線程安全的,單例不是為了保證線程安全。
        /// </summary>
        public void Increment()
        {
            //lock (lockSingleton)
            //{
            this.iTotal++;
            //}
        }

        public static void Show()
        {
            Console.WriteLine(_singleton.iTotal);
        }
    }
}      

使用如下:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace SingletonPattern
{
    /// <summary>
    /// 為什麼要有單例設計模式?
    /// 構造對象耗時耗資源,很多地方都需要去new, 這個方法 其他方法  其他類
    /// </summary>
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                {
                    //保證程序中,某個類隻有一個執行個體
                    //1 構造函數私有化 避免别人還去new
                    //2 公開的靜态方法提供對象執行個體
                    //3 初始化一個靜态字段用于傳回 保證全局都是這一個
                    Singleton singleton1 = Singleton.CreateInstance();
                    Singleton singleton2 = Singleton.CreateInstance();
                    Singleton singleton3 = Singleton.CreateInstance();
                    Console.WriteLine(object.ReferenceEquals(singleton1, singleton2));
                    Console.WriteLine(object.ReferenceEquals(singleton3, singleton2));
                }

                {
                    List<Task> tasks = new List<Task>();
                    for (int i = 0; i < 10000; i++)
                    {
                        tasks.Add(Task.Run(() =>
                        {
                            Singleton singleton = Singleton.CreateInstance();
                            singleton.Increment();
                        }));
                    }
                    Task.WaitAll(tasks.ToArray());
                    Singleton.Show();
                    //即使是單例,變量也不是線程安全的,單例不是為了保證線程安全。
                    //iTotal 是0  1   10000  還是其他的
                    //結果為:其他值,1到10000範圍内都可能   線程不安全
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

            Console.Read();
        }
    }
}      

1.2、餓漢式寫法(靜态構造函數)

using System;
using System.Threading;

namespace SingletonPattern
{
    /// <summary>
    /// 餓漢式
    /// </summary>
    public class SingletonSecond
    {
        private static SingletonSecond _singletonSecond = null;

        /// <summary>
        /// 構造函數耗時耗資源
        /// </summary>
        private SingletonSecond()
        {
            long lResult = 0;
            for (int i = 0; i < 10000000; i++)
            {
                lResult += i;
            }
            Thread.Sleep(1000);
            Console.WriteLine("{0}被構造一次", this.GetType().Name);
        }

        /// <summary>
        /// 靜态構造函數:由CLR保證,程式第一次使用這個類型前被調用,且隻調用一次。
        /// </summary>
        static SingletonSecond()
        {
            _singletonSecond = new SingletonSecond();
            Console.WriteLine("SingletonSecond 被啟動");
        }

        /// <summary>
        /// 餓漢式  隻要使用類就會被構造
        /// </summary>
        /// <returns>
        /// </returns>
        public static SingletonSecond CreateInstance()
        {
            return _singletonSecond;
        }
    }
}      

1.3、餓漢式寫法(靜态字段)

using System;
using System.Threading;

namespace SingletonPattern
{
    /// <summary>
    /// 餓漢式
    /// </summary>
    public class SingletonThird
    {
        /// <summary>
        /// 靜态字段:在第一次使用這個類之前,由CLR保證,初始化且隻初始化一次。
        /// 這個比構造函數還早
        /// </summary>
        private static SingletonThird _singletonThird = new SingletonThird(); //列印個日志

        /// <summary>
        /// 構造函數耗時耗資源
        /// </summary>
        private SingletonThird()
        {
            long lResult = 0;
            for (int i = 0; i < 10000000; i++)
            {
                lResult += i;
            }
            Thread.Sleep(1000);
            Console.WriteLine("{0}被構造一次", this.GetType().Name);
        }

        /// <summary>
        /// 餓漢式  隻要使用類就會被構造
        /// </summary>
        /// <returns>
        /// </returns>
        public static SingletonThird CreateInstance()
        {
            return _singletonThird;
        }

        public void Show()
        {
            Console.WriteLine("這裡是{0}.Show", this.GetType().Name);
        }
    }
}      

2、原型模式(Prototype Pattern)

原型模式:

  換個方式建立對象,不走構造函數,而是記憶體拷貝。

  單例的基礎上更新了一下,把對象從記憶體層面複制了一下,然後傳回。

  是個新對象,但是又不是new出來的。

using System;
using System.Threading;

namespace PrototypePattern
{
    /// <summary>
    /// 原型模式:單例的基礎上更新了一下,把對象從記憶體層面複制了一下,然後傳回。
    /// 是個新對象,但是又不是new出來的。
    /// </summary>
    public class Prototype
    {
        /// <summary>
        /// 構造函數耗時耗資源
        /// </summary>
        private Prototype()
        {
            long lResult = 0;
            for (int i = 0; i < 10000000; i++)
            {
                lResult += i;
            }
            Thread.Sleep(2000);
            Console.WriteLine("{0}被構造一次", this.GetType().Name);
        }

        /// <summary>
        /// 全局唯一靜态  重用這個變量
        /// </summary>
        private static volatile Prototype _prototype = new Prototype();

        /// <summary>
        /// 公開的靜态方法提供對象執行個體
        /// </summary>
        /// <returns>
        /// </returns>
        public static Prototype CreateInstance()
        {
            Prototype prototype = (Prototype)_prototype.MemberwiseClone(); //從記憶體層面複制
            return prototype;
        }
    }
}      

下面為了示範鼎鼎大名的三大工廠我們建立幾個接口和類:

/// <summary>
/// 種族
/// </summary>
public interface IRace
{
    void ShowKing();
}

/// <summary>
/// 軍隊
/// </summary>
public interface IArmy
{
    void ShowArmy();
}

/// <summary>
/// 英雄
/// </summary>
public interface IHero
{
    void ShowHero();
}

/// <summary>
/// 資源
/// </summary>
public interface IResource
{
    void ShowResource();
}

/// <summary>
/// 幸運值
/// </summary>
public interface ILuck
{
    void ShowLuck();
}      
using System;
using FactoryPattern.War3.Interface;

namespace FactoryPattern.War3.Service
{
    /// <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");
        }
    }

    public class HumanArmy : IArmy
    {
        public void ShowArmy()
        {
            Console.WriteLine("The Army of {0} is {1}", this.GetType().Name, "footman,火槍,騎士,獅鹫");
        }
    }

    public class HumanHero : IHero
    {
        public void ShowHero()
        {
            Console.WriteLine("The Army of {0} is {1}", this.GetType().Name, "大法師、山丘、聖騎士、血法師");
        }
    }

    public class HumanResource : IResource
    {
        public void ShowResource()
        {
            Console.WriteLine("The Army of {0} is {1}", this.GetType().Name, "1000G1000W");
        }
    }
}      
using System;
using FactoryPattern.War3.Interface;

namespace FactoryPattern.War3.Service
{
    /// <summary>
    /// 不死族(War3種族之一)
    /// </summary>
    public class Undead : IRace
    {
        public void ShowKing()
        {
            Console.WriteLine("The King of {0} is {1}", this.GetType().Name, "GoStop");
        }
    }

    public class UndeadArmy : IArmy
    {
        public void ShowArmy()
        {
            Console.WriteLine("The Army of {0} is {1}", this.GetType().Name, "食屍鬼,蜘蛛,雕像,戰車,憎惡,冰霜巨龍");
        }
    }

    public class UndeadHero : IHero
    {
        public void ShowHero()
        {
            Console.WriteLine("The Army of {0} is {1}", this.GetType().Name, "DK、Lich、小強、恐懼魔王");
        }
    }

    public class UndeadResource : IResource
    {
        public void ShowResource()
        {
            Console.WriteLine("The Army of {0} is {1}", this.GetType().Name, "1000G1000W");
        }
    }
}      
using System;
using FactoryPattern.War3.Interface;

namespace FactoryPattern.War3.Service
{
    /// <summary>
    /// War3種族之一
    /// </summary>
    public class ORC : IRace
    {
        public void ShowKing()
        {
            Console.WriteLine("The King of {0} is {1}", this.GetType().Name, "Grubby");
        }
    }

    public class ORCArmy : IArmy
    {
        public void ShowArmy()
        {
            Console.WriteLine("The Army of {0} is {1}", this.GetType().Name, "大G、風騎士、蝙蝠、戰車、牛頭人");
        }
    }

    public class ORCHero : IHero
    {
        public void ShowHero()
        {
            Console.WriteLine("The Army of {0} is {1}", this.GetType().Name, "劍聖、小薩滿、先知、牛頭人酋長");
        }
    }

    public class ORCResource : IResource
    {
        public void ShowResource()
        {
            Console.WriteLine("The Army of {0} is {1}", this.GetType().Name, "1000G1000W");
        }
    }
}      
using System;
using FactoryPattern.War3.Interface;

namespace FactoryPattern.War3.Service
{
    /// <summary>
    /// War3種族之一
    /// </summary>
    public class NE : IRace
    {
        public void ShowKing()
        {
            Console.WriteLine("The King of {0} is {1}", this.GetType().Name, "Moon");
        }
    }
}      

3、簡單工廠(Simple Factory)

簡單工廠:不直接new,把對象建立轉移到工廠類。(簡單工廠不屬于23種設計模式)

核心代碼如下:

using System;
using FactoryPattern.War3.Interface;
using FactoryPattern.War3.Service;

namespace SimpleFactory
{
    /// <summary>
    /// 簡單工廠
    /// </summary>
    public class ObjectFactory
    {
        /// <summary>
        /// 細節沒有消失  隻是轉移
        /// 轉移了沖突,并沒有消除沖突
        /// 此處集中了沖突
        /// </summary>
        /// <param name="raceType">
        /// </param>
        /// <returns>
        /// </returns>
        public static IRace CreateRace(RaceType raceType)
        {
            IRace iRace;
            switch (raceType)
            {
                case RaceType.Human:
                    iRace = new Human();
                    break;
                case RaceType.Undead:
                    iRace = new Undead();
                    break;
                case RaceType.ORC:
                    iRace = new ORC();
                    break;
                case RaceType.NE:
                    iRace = new NE();
                    break;
                //每增加一個分支就需要修改代碼
                default:
                    throw new Exception("wrong raceType");
            }

            return iRace;
        }
    }

    /// <summary>
    /// 種族類型枚舉
    /// </summary>
    public enum RaceType
    {
        Human,
        Undead,
        ORC,
        NE
    }
}      
using System;
using FactoryPattern.War3.Interface;

namespace SimpleFactory
{
    /// <summary>
    /// 玩家
    /// </summary>
    public class Player
    {
        public int Id { get; set; }
        public string Name { get; set; }

        /// <summary>
        /// 面向抽象
        /// </summary>
        /// <param name="race">
        /// 種族
        /// </param>
        public void PlayWar3(IRace race)
        {
            Console.WriteLine("******************************");
            Console.WriteLine("This is {0} Play War3.{1}", this.Name, race.GetType().Name);
            race.ShowKing();
        }
    }
}      
using System;
using FactoryPattern.War3.Interface;
using FactoryPattern.War3.Service;

namespace SimpleFactory
{
    /// <summary>
    /// 簡單工廠:非常簡單的工廠
    /// 工廠就是建立對象的地方
    /// </summary>
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Player player = new Player()
                {
                    Id = 123,
                    Name = "候鳥"
                };

                {
                    Human human = new Human();//1 到處都是細節
                    player.PlayWar3(human);
                }

                {
                    IRace human = new Human();//2 左邊是抽象  右邊是細節
                    player.PlayWar3(human);
                }

                {
                    IRace human = ObjectFactory.CreateRace(RaceType.Human); //3 沒有細節  細節被轉移
                    player.PlayWar3(human);
                }

                {
                    IRace undead = ObjectFactory.CreateRace(RaceType.Undead); //4 沒有細節 細節被轉移
                    player.PlayWar3(undead);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

            Console.Read();
        }
    }
}      

4、工廠方法模式(Factory Method Pattern)

工廠方法模式:

  屏蔽對象的建立,留下了擴充空間。

  把簡單工廠拆分成多個工廠,保證每個工廠的相對穩定。

  多new一次工廠,難免,中間層,屏蔽業務類變化的影響,而且可以留下建立對象的擴充空間。

using FactoryPattern.War3.Interface;

namespace FactoryMethod.Factory
{
    public interface IFactory
    {
        /// <summary>
        /// 建立種族
        /// </summary>
        /// <returns>
        /// </returns>
        IRace CreateRace();
    }
}      
using System;
using FactoryPattern.War3.Interface;
using FactoryPattern.War3.Service;

namespace FactoryMethod.Factory
{
    public class HumanFactory : IFactory
    {
        public virtual IRace CreateRace()
        {
            return new Human();
        }
    }

    /// <summary>
    /// 後期可以對其擴充
    /// </summary>
    public class HumanFactoryAdvanced : HumanFactory
    {
        public override IRace CreateRace()
        {
            Console.WriteLine("123");
            return new Human();
        }
    }
}      
using FactoryPattern.War3.Interface;
using FactoryPattern.War3.Service;

namespace FactoryMethod.Factory
{
    public class UndeadFactory : IFactory
    {
        public IRace CreateRace()
        {
            return new Undead();
        }
    }
}      
using FactoryPattern.War3.Interface;
using FactoryPattern.War3.Service;

namespace FactoryMethod.Factory
{
    public class ORCFactory : IFactory
    {
        public IRace CreateRace()
        {
            return new ORC();
        }
    }
}      
using FactoryPattern.War3.Interface;
using FactoryPattern.War3.Service;

namespace FactoryMethod.Factory
{
    public class NEFactory : IFactory
    {
        public IRace CreateRace()
        {
            return new NE();
        }
    }
}      
using System;
using FactoryMethod.Factory;
using FactoryPattern.War3.Interface;

namespace FactoryMethod
{
    /// <summary>
    /// 工廠方法:把簡單工廠拆分成多個工廠,保證每個工廠的相對穩定。
    /// 但是要多new一次工廠? 難免,中間層,屏蔽業務類變化的影響,而且可以留下建立對象的擴充空間。
    /// 開閉原則:對擴充開發,對修改封閉。
    /// 工廠方法完美遵循了開閉原則
    /// </summary>
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                {
                    //human
                    IFactory factory = new HumanFactory();//包一層
                    IRace race = factory.CreateRace();
                    //何苦  搞了這麼多工廠 還不是建立個對象
                    //以前依賴的是Human  現在換成了HumanFactory
                    //1 工廠可以增加一些建立邏輯  屏蔽對象執行個體化的複雜度
                    //2 對象建立的過程中  可能擴充(尤其是ioc)
                }

                {
                    //Undead
                    IFactory factory = new UndeadFactory();
                    IRace race = factory.CreateRace();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

            Console.Read();
        }
    }
}      

可以看出工廠方法模式,就是把簡單工廠拆分成多個工廠,保證每個工廠的相對穩定。多new一次工廠,難免,中間層,屏蔽業務類變化的影響,而且可以留下建立對象的擴充空間。

另外工廠方法完美遵循了開閉原則,例如:Demo中原先我們有4個種族,分别為Human、Undead、ORC和NE,此時如果業務發生變化需要增加一個Five種族,

這時候我們隻需要添加一個Five工廠類就好了,不會影響原來的代碼。

using System;
using FactoryPattern.War3.Interface;

namespace FactoryPattern.War3.ServiceExtend
{
    /// <summary>
    /// War3種族之一
    /// </summary>
    public class Five : IRace
    {
        public Five()
            : this(1, "old", 1) //目前類的構造函數
        {

        }

        public Five(int id, string name, int version)
        {

        }

        public void ShowKing()
        {
            Console.WriteLine("The King of {0} is {1}", this.GetType().Name, "Moon");
        }
    }
}      
using FactoryPattern.War3.Interface;
using FactoryPattern.War3.ServiceExtend;

namespace FactoryMethod.Factory
{
    /// <summary>
    /// 比如構造很複雜。。比如依賴其他對象
    /// 屏蔽變化
    /// </summary>
    public class FiveFactory : IFactory
    {
        public virtual IRace CreateRace()
        {
            //return new Five();
            return new Five(2, "New", 2);
        }
    }
}      

5、抽象工廠模式(Abstract Factory Pattern)

抽象工廠模式:

  屏蔽對象的建立,限制強制保障産品簇。

  建立一組密不可分的對象。

  建立産品簇:建立一組密不可分的對象。

  工廠+限制

  傾斜的可擴充性設計,擴充種族很友善,增加産品很麻煩。

 核心代碼如下:

using FactoryPattern.War3.Interface;

namespace AbstractFactory.Factory
{
    /// <summary>
    /// 一個工廠負責一些産品的建立
    /// 産品簇
    /// 單一職責就是建立完整的産品簇
    /// 
    /// 繼承抽象類後,必須顯式的override父類的抽象方法。
    /// </summary>
    public abstract class FactoryAbstract
    {
        public abstract IRace CreateRace();
        public abstract IArmy CreateArmy();
        public abstract IHero CreateHero();
        public abstract IResource CreateResource();

        //傾斜的可擴充性設計:擴充種族很友善,增加産品(元素)很麻煩。
        //public abstract ILuck CreateLuck(); //增加産品(元素)則每個種族的代碼都要修改
    }
}      
using FactoryPattern.War3.Interface;
using FactoryPattern.War3.Service;

namespace AbstractFactory.Factory
{
    /// <summary>
    /// 一個工廠負責一些産品的建立
    /// </summary>
    public class HumanFactory : FactoryAbstract
    {
        public override IRace CreateRace()
        {
            return new Human();
        }

        public override IArmy CreateArmy()
        {
            return new HumanArmy();
        }

        public override IHero CreateHero()
        {
            return new HumanHero();
        }

        public override IResource CreateResource()
        {
            return new HumanResource();
        }
    }
}      
using FactoryPattern.War3.Interface;
using FactoryPattern.War3.Service;

namespace AbstractFactory.Factory
{
    /// <summary>
    /// 一個工廠負責一些産品的建立
    /// </summary>
    public class UndeadFactory : FactoryAbstract
    {
        public override IRace CreateRace()
        {
            return new Undead();
        }

        public override IArmy CreateArmy()
        {
            return new UndeadArmy();
        }

        public override IHero CreateHero()
        {
            return new UndeadHero();
        }

        public override IResource CreateResource()
        {
            return new UndeadResource();
        }
    }
}      
using System;
using AbstractFactory.Factory;
using FactoryPattern.War3.Interface;
using FactoryPattern.War3.Service;

namespace AbstractFactory
{
    /// <summary>
    /// 抽象工廠:建立一組密不可分的對象。
    /// 建立産品簇:多個對象是個整體,不可分割。
    /// 
    /// 工廠+限制
    /// 
    /// 傾斜的可擴充性設計:擴充種族很友善,增加産品(元素)很麻煩。
    /// </summary>
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Console.WriteLine("想要玩一款遊戲,必須4大元素備齊");

                //System.Data.SqlClient.SqlClientFactory //使用的就是抽象工廠模式
                {
                    IRace race = new Undead();
                    IArmy army = new UndeadArmy();
                    IHero hero = new UndeadHero();
                    IResource resource = new UndeadResource();
                    //1 對象轉移,屏蔽細節,讓使用者更輕松
                    //2 對象簇的工廠
                }

                {
                    FactoryAbstract undeadFactory = new UndeadFactory();
                    IRace race = undeadFactory.CreateRace();// new Undead();
                    IArmy army = undeadFactory.CreateArmy();//new UndeadArmy();
                    IHero hero = undeadFactory.CreateHero();//new UndeadHero();
                    IResource resource = undeadFactory.CreateResource();//new UndeadResource();
                }

                {
                    FactoryAbstract humanFactory = new HumanFactory();
                    IRace race = humanFactory.CreateRace();
                    IArmy army = humanFactory.CreateArmy();
                    IHero hero = humanFactory.CreateHero();
                    IResource resource = humanFactory.CreateResource();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

            Console.Read();
        }
    }
}      

可以看出抽象工廠模式,屏蔽對象的建立,限制強制保障産品簇,建立一組密不可分的對象(例如:Demo中每個種族都有四個密不可分的元素,分别為Race、Army、Hero和Resource)。

另外抽象工廠模式是傾斜的可擴充性設計,擴充種族很友善,增加産品(元素)很麻煩,例如在Demo中如果産品簇增加一個Luck元素則每個種族的代碼都需要修改。

6、建造者模式(Builder Pattern)

建造者模式:複雜的工廠方法。

Demo源碼:

連結:https://pan.baidu.com/s/1x_quZxEUpCmqqc3Gf-QlFg 
提取碼:h1py      

此文由部落客精心撰寫轉載請保留此原文連結:https://www.cnblogs.com/xyh9039/p/13258500.html

版權聲明:如有雷同純屬巧合,如有侵權請及時聯系本人修改,謝謝!!!

繼續閱讀