如何管理对可交换类的订阅
我试图获得更好的感觉,以便如何维护可能交换的类的订阅(更改策略)。即使这些例子是人为设计的,我也会尽量保持这一点。如何管理对可交换类的订阅
假设有一类皮肤
public class Skin {
//Raised when the form needs to turn on/off a blinking light
public event BlinkEventHandler BlinkEvent;
//The back color that forms should use
public Color BackColor{ get; protected set; }
}
应用程序启动时,它会读取的目录全配置文件用于不同的皮肤类。用户可以随时切换当前皮肤。
我目前的工作使用了一个非常奇怪的策略(IMO),看起来像这样:
/// <summary> /// Some class that can see when the Skin Changes
/// </summary>
public class SkinManager
{
//Raised when the Skin changes
public event SkinChangedEventHandler SkinChangedEvent;
private static Skin currentSkin;
public static Skin CurrentSkin {get;}
public SkinManager(){/* gets a skin into currentSkin */}
public void ChangeSkin()
{
//... do something to change the skin
if(SkinChangedEvent != null)
{
SkinChangedEvent(this, new SkinChangedEventArgs(/*args*/));
}
}
}
/// <summary>
/// Some form that follows the Skinning Strategy
/// </summary>
public class SkinnedForm : Form
{
private Skin skin;
public SkinnedForm()
{
skin = SkinManager.CurrentSkin;
if(skin != null)
{
skin.BlinkEvent += OnBlink;
}
SkinManager.SkinChangedEvent += OnSkinChanged;
}
private void OnSkinChanged(object sender, SkinChangedEventArgs e)
{
//unregister if we have a current skin
//the local was to ensure that the form unsubscribes
//when skin changes
if(skin != null)
{
skin.BlinkEvent -= OnBlink;
}
skin = SkinManager.CurrentSkin;
if(skin != null)
{
skin.BlinkEvent += OnBlink;
}
SkinChanged();
}
private void SkinChanged(){ Invalidate(); }
private void OnBlink(object sender, BlinkEventArgs e)
{
//... do something for blinking
}
}
我无法相信这是一个很好的实施,反而会希望看到这样的事情:
/// <summary> /// Some class that can see when the Skin Changes
/// </summary>
public class SkinManager
{
//Raised when the Skin changes
public event SkinChangedEventHandler SkinChangedEvent;
//Relays the event from Skin
public event BlinkEventHander BlinkEvent;
private static Skin currentSkin;
public static Skin CurrentSkin {get;}
public SkinManager()
{
//... gets a skin into currentSkin
currentSkin.BlinkEvent += OnBlink;
}
/// <summary>
/// Relays the event from Skin
/// </summary>
private void OnBlink(object sender, BlinkEventArgs e)
{
if(BlinkEvent != null)
{
BlinkEvent(this, e);
}
}
public void ChangeSkin()
{
//... do something to change the skin
if(SkinChangedEvent != null)
{
SkinChangedEvent(this, new SkinChangedEventArgs(/*args*/));
}
}
}
/// <summary>
/// Some form that follows the Skinning Strategy
/// </summary>
public class SkinnedForm : Form
{
//Do not need the local anymore
//private Skin skin;
public SkinnedForm()
{
SkinManager.CurrentSkin.BlinkEvent += OnBlink;
SkinManager.SkinChangedEvent += OnSkinChanged;
}
private void OnSkinChanged(object sender, SkinChangedEventArgs e)
{
//Only register with the manager, so no need to deal with
//subscription maintenance, could just directly to go SkinChanged();
SkinChanged();
}
private void SkinChanged() { Invalidate(); }
private void OnBlink(object sender, BlinkEventArgs e)
{
//... do something for blinking
}
}
我不确定这是否清楚,但主要是有一个局部变量,严格使用,以确保我们在订阅新类上的事件之前取消订阅事件。我认为它是这样的:我们实施了蒙皮策略模式(选择您想要使用的蒙皮策略并运行它),但是每个策略实施都有我们直接订阅的事件。当战略发生变化时,我们希望我们的订阅者观看正确的发布者,以便我们使用当地人。再次,我认为这是一种可怕的方法。
是否有一个我为使用管理器来监控所管理的类的所有事件并将它们传递的策略而改变并且订户继续侦听正确事件通知的转换的名称?所提供的代码是在我形成问题时即时创建的,因此可以原谅任何错误。
回答:
通常,当您想要为引发事件的类创建代理(包装器)时,您需要取消提交(分离)前一个实例,交换一个新实例,然后订阅(附加)其事件。
比方说,你的皮肤界面看起来是这样的:
interface ISkin {
void RenderButton(IContext ctx);
event EventHandler Blink;
}
然后,你改变它需要看起来像这样的部分:
public void SetSkin(ISkin newSkin) {
// detach handlers from previous instance
DetachHandlers();
// swap the instance
_skin = newSkin;
// attach handlers to the new instance
AttachHandlers();
}
void DetachHandlers()
{
if (_skin != null)
_skin.Blink -= OnBlink;
}
void AttachHandlers()
{
if (_skin != null)
_skin.Blink += OnBlink;
}
完全代理会是这个样子:
interface IChangeableSkin : ISkin {
event EventHandler SkinChanged;
}
public class SkinProxy : IChangeableSkin
{
private ISkin _skin; // actual underlying skin
public void SetSkin(ISkin newSkin)
{
if (newSkin == null)
throw new ArgumentNullException("newSkin");
if (newSkin == _skin)
return; // nothing changed
// detach handlers from previous instance
DetachHandlers();
// swap the instance
_skin = newSkin;
// attach handlers to the new instance
AttachHandlers();
// fire the skin changed event
SkinChanged(this, EventArgs.Empty);
}
void DetachHandlers()
{
if (_skin != null)
_skin.BlinkEvent -= OnBlink;
}
void AttachHandlers()
{
if (_skin != null)
_skin.BlinkEvent += OnBlink;
}
void OnBlink(object sender, EventArgs e)
{
// just forward the event
BlinkEvent(this, e);
}
// constructor
public SkinProxy(ISkin initialSkin)
{
SetSkin(initialSkin);
}
#region ISkin members
public void RenderButton(IContext ctx)
{
// just calls the underlying implementation
_skin.RenderButton(ctx);
}
// this is fired inside OnBlink
public event EventHandler BlinkEvent = delegate { };
#endregion
#region IChangeableSkin members
public event EventHandler SkinChanged = delegate { };
#region
}
您的表单应该只包含对实施的引用。
回答:
种类繁杂,切换负担由订户承担。这不太好。
当交换外观时,旧皮肤可能会删除其事件订户,并可能将它们附加到新外观。
但一个整洁的图案可能是一个皮肤的持有人不会改变,并暴露事件。
回答:
SkinnedForm可以有型I皮肤的属性 -
public class SkinnedForm : Form {
private ISkin _Skin;
...
}
通过公共财产暴露这一点,并且在任何时候设置。这样,SkinnedForm永远不会关心ISkin如何工作,或者它包含的事件模型。当您传入新的Skin类别参考时,新的OnBlink事件将自动接管。实现ISkin的类应该包含OnBlink的逻辑。
然后,您将拥有一个管理员类(与您指定的距离不太远)可以获得对新外观的引用,以及相关的SkinnedForm。经理的唯一工作是更新SkinnedForm上的ISkin属性。
以上是 如何管理对可交换类的订阅 的全部内容, 来源链接: utcz.com/qa/267150.html