选择适当对象和适当方法的策略

时间:2010-04-15 04:14:05

标签: c# .net

在下面的代码中,首先是if语句块(不仅仅是“worker”条件,加上else if)我选择了正确的filter_object。在此相同的条件块之后,我选择过滤器对象应该应用什么过滤器。这段代码很傻。

public class Filter
{
    public static List<data.Issue> fetch(string type, string filter)
    {
        Filter_Base filter_object = new Filter_Base(filter);

        if (type == "worker")
        {
            filter_object = new Filter_Worker(filter);
        }
        else if (type == "dispatcher")
        {
            filter_object = new Filter_Dispatcher(filter);
        }

        List<data.Issue> result = new List<data.Issue>();

        if (filter == "new")
        {
            result = filter_object.new_issues();
        }
        else if (filter == "ended")
        {
            result = filter_object.ended_issues();
        }

        return result;
    }
}

public class Filter_Base
{
    protected string _filter;

    public Filter_Base(string filter)
    {
        _filter = filter;
    }

    public virtual List<data.Issue> new_issues()
    {
        return new List<data.Issue>();
    }

    public virtual List<data.Issue> ended_issues()
    {
        return new List<data.Issue>();
    }
}

public class Filter_Worker : Filter_Base
{
    public Filter_Worker(string filter) :
        base(filter)
    { }

    public override List<data.Issue> new_issues()
    {
        return (from i in data.db.GetInstance().Issues
                where (new int[] { 4, 5 }).Contains(i.RequestStatusId)
                select i).Take(10).ToList();
    }
}

public class Filter_Dispatcher : Filter_Base
{
    public Filter_Dispatcher(string filter) :
        base(filter)
    { }
}

它将用于某种形式:

Filter.fetch("worker", "new");

这段代码意味着,对于属于角色“工作者”的用户,只会获取“新”问题(这是某种小而简单的CRM)。或者另一个:

Filter.fetch("dispatcher", "ended"); // here we get finished issues for dispatcher role

有关如何改进它的任何建议吗?

3 个答案:

答案 0 :(得分:2)

我假设你问你如何修剪Fetch方法。我会使用泛型

public static List<data.Issue> Fetch<T>( string filter ) where T : FilterBase, new()
{
    var filterBase = new T();
    filterBase.Initialize( filter );

    List<data.Issue> result;
    if ( IsNew( filter ) )
        result = filterBase.NewIssues();
    else if ( IsEnded( filter ) )
        result = filterBase.EndedIssues();
    else
        result = new List<data.Issue>();

    return result;
}

这需要:

  1. FilterBase和派生类的无参数公共(或至少内部)构造函数。
  2. 一个名为Initialize的虚方法,它在FilterBase上接受一个字符串。
  3. Filter类上的IsNew和IsEnded静态函数,以便Fetch方法可以确定您可以使用哪个函数。另一个解决方案是Fetch的附加委托参数,以确定应该调用哪个方法。
  4. 如何改进代码的其余部分?

    1. 停止在类和方法名称(和字段名称)中使用下划线。
    2. 使用Pascal Case作为方法名称(因此NewIssues而不是new_issues)
    3. 除非有令人信服的理由,否则请将您的基类抽象化。
    4. 找到更明确的方法来确定“新”和“结束”。也许是一个额外的枚举参数?

答案 1 :(得分:1)

这可能看起来很长,但我会利用属性和反射来尝试实现这一目标。

这样的东西

属性:

class FilterType : Attribute
{
    public string Filter;
    public FilterType(string filter)
    {
        Filter = filter;
    }
}

class FilterMethod : Attribute
{
    public string Filter;
    public FilterMethod(string filter)
    {
        Filter = filter;
    }
}

具有属性标记的类

public class Filter_Base
{
    protected string _filter;

    public Filter_Base(string filter)
    {
        _filter = filter;
    }

    [FilterMethod("new")]
    public virtual List<string> new_issues()
    {
        return new List<string>();
    }
}
[FilterType("Worker")]
public class Filter_Worker : Filter_Base
{
    public Filter_Worker(string filter) :
        base(filter)
    { }

    public override List<string> new_issues()
    {
        return new List<string>();
    }
} 

过滤方法

private static List<string> GetInstanceList(string type, string filter)
{
    //get all classes implementing FilterType Attribute
    var dict = AppDomain.CurrentDomain.GetAssemblies().
                SelectMany(x => x.GetTypes()).
                Where(x => x.GetCustomAttributes(typeof(FilterType), false).Length > 0).
                Select(x => new { ((FilterType)x.GetCustomAttributes(typeof(FilterType), false)[0]).Filter, x }).
                ToDictionary(x => x.Filter);

    Filter_Base instance = (Filter_Base)Activator.CreateInstance(dict[type].x, filter);

    var methods = instance.GetType().GetMembers().
                    Where(x => x.GetCustomAttributes(typeof(FilterMethod), true).Length > 0).
                    Select(x => new { ((FilterMethod)x.GetCustomAttributes(typeof(FilterMethod), true)[0]).Filter, x }).
                    ToDictionary(x => x.Filter);


    return (List<string>)instance.GetType().GetMethod(methods[filter].x.Name).Invoke(instance, null);
}

最后致电

List<string> instance = GetInstanceList("Worker", "new");

答案 2 :(得分:1)

如果有大量的FilterBase衍生物,DI是可能的。根据你选择的武器代码看起来类似于:

ioc
  .RegisterType<IDataStrategy, FilterWorker>("worker")
  /// more filters
  .RegisterType<IDataStrategy, FilterDispatcher>("dispatcher");

或者工厂对象至少可以重构你的代码,但我离题了......

当你在它时,丢失基类并实现一个接口:

public interface IDataStrategy
{
    IEnumerable<Data.Issue> FetchNew();
    IEnumerable<Data.Issue> FetchEnded(); 
}

按建议使用枚举:

public enum FilterType 
{
    New,
    Ended
}

现在在你的获取功能

public IEnumerable<Data.Issue> Fetch(string dataType, FilterType filterType)
{ 
    var strategy = ioc.Resolve<IDataStrategy>(dataType);
    IEnumerable<Data.Issue> results = null;
    switch(filterType)
    {
        case FilterType.New:
            results = strategy.FetchNew();
        default:
            results = strategy.Ended();
    }
    return results; 
}