1. 程式人生 > >以引用對象代替單例模式

以引用對象代替單例模式

ace 這樣的 new 引入 -s 函數 span mov 減少

介紹:

系統中存在單例的全局訪問點,你希望將對單例的訪問通過對象引用來實現。往往是將對單例的依賴關系轉換為關聯關系。

動機:

在系統中引入單例模式往往並沒有起到明顯的效果卻添加了系統的復雜性。不能只由於某個類只須要一個實例而採用單例模式。這些全然能夠用引用對象代替。 通過全局訪問點使用單例對象往往造成依賴不清、可讀性差等問題,我們全然能夠通過顯式的關聯引用來做到在子系統中共享同一個實例,而且僅僅對須要這個實例的對象註入依賴。將對全局變量的依賴轉變為對成員對象的依賴使類更易於理解。 當設計須要中不須要強調系統的不論什麽時刻都僅僅須要同一實例的時候,單例模式最大的缺點在於沒有長處。

單例模式並非經常使用的設計模式。


重構:

1. 改動單例類,將構造函數設為public,取消訪問方法和相關屬性。
2. 查找類的全部引用點,加入對單例類的引用字段並將全局訪問改為對引用對象的請求。

3. 改動相關類的構造函數。將引用對象作為參數傳遞進來。

4. 查找相關類的引用點,添加構造函數的調用參數。

範例:

如果有這樣一個類:它封裝了輸入,外觀層改動類的狀態,並使詳細類能夠不同步地訪問此類。

這種封裝減少了外觀類與詳細類的耦合。


因為輸入類僅僅須要一個實例,能夠採用單例模式實現這樣的設計(不考慮線程安全):

    /// <summary>
    /// 封裝用戶輸入
    /// </summary>
    public class Input
    {
        private Input() { }

        /// <summary>
        /// 用戶輸入實例
        /// </summary>
        private static Input _instance;

        /// <summary>
        /// 獲取封裝用戶輸入的唯一實例
        /// </summary>
        public static Input Instance
        {
            get 
            {
                if (_instance == null)
                {
                    _instance = new Input();
                }
                return _instance;
            }
        }

        /// <summary>
        /// 綁定到窗口
        /// </summary>
        /// <param name="window">對應用戶輸入的窗口</param>
        public void BindingToForm(Form window)
        {
            KeyBoard = new KeyBoard(window);
        }

        /// <summary>
        /// 獲取或設置鍵盤輸入狀態
        /// </summary>
        public KeyBoard KeyBoard { get; private set; }

        /// <summary>
        /// 更新輸入狀態
        /// </summary>
        public void Update()
        {
            KeyBoard.Process();
        }
    }

通過窗口事件獲取輸入數據。並直接反應在這個唯一實例中。
如今用引用對象代替單例模式,則Input類改為:
    /// <summary>
    /// 封裝用戶輸入
    /// </summary>
    public class Input
    {
        /// <summary>
        /// 綁定到窗口
        /// </summary>
        /// <param name="window">對應用戶輸入的窗口</param>
        public void BindingToForm(Form window)
        {
            KeyBoard = new KeyBoard(window);
        }

        /// <summary>
        /// 獲取或設置鍵盤輸入狀態
        /// </summary>
        public KeyBoard KeyBoard { get; private set; }

        /// <summary>
        /// 更新輸入狀態
        /// </summary>
        public void Update()
        {
            KeyBoard.Process();
        }
    }

詳細類擁有對Input實例的引用,通過異步的方式與外觀類共同使用Input對象.
    class SpaceShip
    {
        Input _input;

        public SpaceShip(Input input)
        {
            this._input = input;
        }

        public Vector MoveDirection { get; set; }

        private Vector _posation;

        public void Move()
        {
            _posation = _posation + MoveDirection;
        }

        public void Update()
        {
            Vector amount = Vector.Zero;

            if (_input.KeyBoard.IsKeyHeld(Keys.Left))
            {
                amount.X = -1;
            }
            if (_input.KeyBoard.IsKeyHeld(Keys.Right))
            {
                amount.X = 1;
            }
            if (_input.KeyBoard.IsKeyHeld(Keys.Up))
            {
                amount.Y = -1;
            }
            if (_input.KeyBoard.IsKeyHeld(Keys.Down))
            {
                amount.Y = 1;
            }
            MoveDirection = amount;
        }
    }

外觀類創建並使用Input對象,並將此對象註入各個須要的類中。在winfrom程序中體現為Form子類組合Input的一個實例,通過用戶界面事件改變Input對象狀態,詳細類能夠通過引用異步的獲取這些狀態。





以引用對象代替單例模式