介绍:
系统中存在单例的全局訪问点,你希望将对单例的訪问通过对象引用来实现。往往是将对单例的依赖关系转换为关联关系。
动机:
在系统中引入单例模式往往并没有起到明显的效果却添加了系统的复杂性。不能只由于某个类只须要一个实例而採用单例模式。这些全然能够用引用对象代替。
通过全局訪问点使用单例对象往往造成依赖不清、可读性差等问题,我们全然能够通过显式的关联引用来做到在子系统中共享同一个实例,而且仅仅对须要这个实例的对象注入依赖。将对全局变量的依赖转变为对成员对象的依赖使类更易于理解。
当设计须要中不须要强调系统的不论什么时刻都仅仅须要同一实例的时候,单例模式最大的缺点在于没有长处。
单例模式并非经常使用的设计模式。
重构:
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对象状态,详细类能够通过引用异步的获取这些状态。