【问题标题】:Composition over inheritance with generics使用泛型进行组合而不是继承
【发布时间】:2012-10-13 12:07:19
【问题描述】:

假设我有两个接口 ISomeInterfaceISomeInterface<T>ISomeInterface<T> 继承自 ISomeInterface

现在,如果我们必须对 SomeclassSomeClass<T> 类进行分类,我们如何实现组合而不是继承?哪个类应该有另一个类的实例?

public interface ISomeInterface
{
     string Name {get;}
}

public interface ISomeInterface<T> : ISomeInterface
{
     string ResultMessage{get;}
}

public class SomeClass : ISomeInterface
{
     public string Name
     {
          get{return this.GetType().Name;
     }
}
public class SomeClass<T> ISomeInterface<T>
{
     ISomeInterface _someclassinstance = new SomeClass;

     public string Name
     {
          get{return _someclassInstance.Name;
     }

     public string Caption
     {
          get{return "Demo Caption";}
     }
}

这不起作用,因为当我在类的通用版本的实例上调用 Name 属性时,它将返回 SomeInterface 而不是派生类名。

我想知道它是否是好的实现或非通用版本应该有与我迄今为止所做的相反的通用实例?

   public abstract class ViewModelBase : NotificationObject, IViewModel
{
    private IViewModel _parent;
    private string _title;
    private bool _isBusy;
    private bool _isDitry;

    #region Viewmodel Initialization steps

    /// <summary>
    /// Initializes a new instance of the <see cref="ViewModelBase"/> class.
    /// </summary>
    internal protected ViewModelBase()
    {
        ResolveDependencies();

        Load();
    }

    /// <summary>
    /// Exposes a way for external users to pass a boxed payload
    /// to this view model for initialization purposes.
    /// Inheritors can override this method to perform specific
    /// initialization and validation operations depending on the
    /// payload sent from the outside.
    /// </summary>
    /// <param name="payload">
    /// Represents the boxed payload the view model uses
    /// for initialization purposes.
    /// </param>
    public virtual void Initialize(object payload)
    {
    }

    /// <summary>
    /// Loads the view model.
    /// </summary>
    private void Load()
    {
        BeforeLoad();
        OnLoad();
    }

    /// <summary>
    /// Gives inheritors the opportunity to perform an operation before the view model loads.
    /// </summary>
    protected virtual void BeforeLoad()
    {
    }

    /// <summary>
    /// Gives inheritors the opportunity to perform specific operations when the view model loads for the first time.
    /// </summary>
    protected virtual void OnLoad()
    {
        SetupEvents();
    }

    /// <summary>
    /// Registers listeners for all events in which the ViewModel is interested in.
    /// </summary>
    protected virtual void SetupEvents()
    {

    }

    /// <summary>
    /// Resolves the dependencies for this view model.
    /// </summary>
    private void ResolveDependencies()
    {
        BeforeResolveDependencies();
        EventAggregator = ServiceLocator.Current.GetInstance<IEventAggregator>();
        OnResolveDependencies();
    }

    /// <summary>
    /// Gives inheritors the opportunity to perform an operation before the dependencies are resolved.
    /// </summary>
    protected virtual void BeforeResolveDependencies()
    {
    }

    /// <summary>
    /// Gives inheritors the opportunity to resolve dependencies of their own.
    /// </summary>
    protected virtual void OnResolveDependencies()
    {
    }

    /// <summary>
    /// Event handler that notifies the new children of the parent
    /// view model they live in and removes the parent when they are
    /// removed from the collection.
    /// </summary>
    /// <param name="sender">Child view model rising the event.</param>
    /// <param name="e">Changes made to the collection.</param>
    protected void ChildCollectionChangedHandler(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems != null)
        {
            foreach (var item in e.NewItems)
            {
                ((ViewModelBase)item).Parent = this;
            }
        }

        if (e.OldItems == null) return;
        foreach (var item in e.OldItems)
        {
            ((ViewModelBase)item).Parent = null;
        }
    }

    #endregion

    #region Members of IViewModel

    /// <summary>
    /// Gets the title.
    /// </summary>
    public string Title
    {
        get { return _title ?? (_title = GetType().Name); }
        protected set
        {
            _title = value;
            RaisePropertyChanged(() => Title);
        }
    }

    /// <summary>
    /// Gets or sets a value indicating whether the ViewModel is busy.
    /// </summary>
    /// <value><see langword="true"/> if the ViewModel is busy; otherwise, <see langword="false"/>.</value>
    public bool IsBusy
    {
        get { return _isBusy; }
        protected set
        {
            _isBusy = value;
            RaisePropertyChanged(() => IsBusy);
        }
    }

    /// <summary>
    /// Gets or sets a value indicating whether this instance is dirty.
    /// </summary>
    /// <value>
    ///   <c>true</c> if this instance is dirty; otherwise, <c>false</c>.
    /// </value>
    public bool IsDirty
    {
        get { return _isDitry; }
        set
        {
            _isBusy = value;
            RaisePropertyChanged(() => IsDirty);
        }
    }

    /// <summary>
    /// Gets or sets the view for which this instance is set as DataContext.
    /// </summary>
    /// <value>
    /// The view.
    /// </value>
    public IView View { get; set; }

    /// <summary>
    /// Gets the parent ViewModel holding this instance as
    /// childe ViewModel collection item.
    /// </summary>
    public IViewModel Parent
    {
        get { return _parent; }
        set
        {
            _parent = value;
            RaisePropertyChanged(() => Parent);
        }
    }

    #endregion

    /// <summary>
    /// Get CurrentInstance of EventAggregator
    /// </summary>
    private IEventAggregator EventAggregator { get; set; }

    /// <summary>
    /// Gets the dispatcher.  Works at run-time and test-time.
    /// </summary>
    /// <value>The dispatcher.</value>
    protected static Dispatcher Dispatcher
    {
        get { return Application.Current != null ? Application.Current.Dispatcher : Dispatcher.CurrentDispatcher; }
    }


    /// <summary>
    /// Resolves this instance for specific type.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    protected static T Resolve<T>()
    {
        try
        {
            return ServiceLocator.Current.GetInstance<T>();
        }
        catch (Exception)
        {
            return default(T);
        }
    }

    /// <summary>
    /// Resolves all instances of specific type.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    protected static IEnumerable<T> ResolveAll<T>()
    {
        return ServiceLocator.Current.GetAllInstances<T>();
    }

    /// <summary>
    /// Registers for specific event inside system event providing some filter parameters.
    /// </summary>
    /// <typeparam name="TEvent">The type of the event.</typeparam>
    /// <param name="onEvent">The on event.</param>
    /// <param name="filter">The filter.</param>
    protected void RegisterForEvent<TEvent>(Action<TEvent> onEvent, Predicate<TEvent> filter = null) where TEvent : class
    {
        EventAggregator.Subscribe<TEvent>(onEvent, ThreadOption.PublisherThread, false, filter);
    }

    /// <summary>
    /// Broadcasts the specified event object.
    /// </summary>
    /// <typeparam name="TEvent">The type of the event.</typeparam>
    /// <param name="eventObject">The event object.</param>
    protected void Broadcast<TEvent>(TEvent eventObject) where TEvent : class
    {
        EventAggregator.Publish(eventObject);
    }
}

这是 IViewModel 基类的完整实现和

    public abstract class ViewModelBase<TValidator> : ViewModelBase,IViewModel<TValidator>, IDataErrorInfo
    where TValidator : IValidator, new()
{
    private bool _islValid;
    readonly IValidator _validator = new TValidator();

    /// <summary>
    /// Get Validation processor for ViewmodelInstance 
    /// </summary>
    /// <value>
    /// The Validator.
    /// </value>
    public TValidator Validator
    {
        get { return (TValidator) _validator; }
    }

    public bool IsValid
    {
        get
        {
            _islValid = SelfValidate().IsValid;
            return _islValid;
        }
        private set
        {
            _islValid = value;                
            RaisePropertyChanged(() => IsValid);
        }
    }
// Implementation for validation
}

我在这里发布了这两个类的源代码。并且有 N 个不同的接口和类,它们都需要从这些类的通用或非通用版本派生。 IViewmodel 中可用的放置属性应该在每个派生类上都可用,并且还需要保留受保护的方法和属性

【问题讨论】:

  • 在那里应用作文有什么意义?
  • 我将有更多的类派生自上述类的 Generic 或 NonGeneric 版本,它们是 ViewModel,因此有一些属性需要 ServicesClasses 以及一些受保护的属性,如 Dispatcher 或通用方法 RegisterForEvent(动作 OnAction);

标签: c# design-patterns generics composition


【解决方案1】:

关于组成,泛型版本需要使用非泛型版本。

你不能走相反的路,因为类的泛型版本是强类型的,这意味着它比非泛型版本包含更多的编译时间细节。没有办法将所需类型从非泛型类传递给内部泛型类。

Name 属性开始,它实际上完全按照它应该做的事情,返回SomeClass 类型的Name

编辑:

如果您需要一种方法来始终返回当前类型的名称,那么有很多方法可以解决这个问题。您可以为此创建一个普通或通用的辅助方法,或者例如,您可以为 ISomeInterface 类型创建一个扩展方法并使用它来代替 Name 属性:

public static class SomeInterfaceExtensions
{
    // Extension
    public static string GetName(this ISomeInterface some)
    {
        return some.GetType().Name;
    }
}

// Usage
var some = new SomeClass<int>();
var name = some.GetName();

但是您将实现的只是创建一个到GetType().Name 的快捷方式,因为所有类型都已经有了这些。

我认为这个解决方案没有多大价值,至少在给定类型名称的示例中。

此外,这与泛型与非泛型或提到的所有其他内容无关。

【讨论】:

  • 是的,我知道,但是如果我想返回当前的班级名称?如何做到这一点?让我解释一下 Some 类是 Viewmodebase 类, SomeClass 是采用 TValidator 的通用版本,因此 WPF 验证有效。但是有一些常见的属性,例如 Dispatcher 和 som init 方法,我希望这两个类都具有 Init、BeforeLoad、OnLoad、BeforeResolveDeppendencies onUnload 等。
  • 也许我必须创建另一个类,它是上述类的 Helper 类,它也实现了 ISomeInterface 并将其作为构造函数参数传递,如果我知道它们从不处理返回 Name 和所有其他实现改变?
  • 那么这是一个有点不同的问题。你是对的,我将添加一个如何实现这一点的示例。
  • 你可以在这里查看更新的代码,这样你就可以看到所有基类是如何构造的。我有不同的类和接口,比如带有 NavigationAware 视图模型和 NavigationAware 的 INavigationAware,所以你在这里得到了基本的想法
  • 好吧,现在我真的不明白您对其他代码的要求以及它与最初给出的示例有何关系。您仍然只提出了一个问题,即关于类的泛型和非泛型版本的组合。
猜你喜欢
  • 1970-01-01
  • 2015-02-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-16
  • 1970-01-01
  • 1970-01-01
  • 2015-09-29
相关资源
最近更新 更多