【问题标题】:help with c# patternc#模式的帮助
【发布时间】:2010-12-21 05:54:31
【问题描述】:

您好,感谢您的帮助。

使用 .Net 3.5 C#;

假设我有大约 10 种方法都遵循相同的模式

以3为例:

public Customer CreateCustomer(Customer c) { .. }
public Car CreateCar(Car c) { .. }
public Planet CreatePlanet(Planet p) { ..}

每个方法的内部逻辑具有完全相同的模式。

IE:

public Customer CreateCustomer(Customer c)
{
Log.BeginRequest(c, ActionType.Create); 
Validate(customer);
WebService.Send(Convert(c));
Log.EndRequest(c, ActionType.Create); 
}

public Car CreateCar(Car c)
{
Log.BeginRequest(c, ActionType.Create); 

Validate(c);

WebService.Send(Convert(c));

Log.EndRequest(c, ActionType.Create); 
}

CreatePlanet 和其他 7 种方法也是如此。

这些方法可以重写吗,它们都遵循相同的模式,我觉得我错过了一些东西......是否可以导出另一个抽象级别?

问题:应该如何重写以利用适当的架构模式?

谢谢, 史蒂文

【问题讨论】:

  • Generic 能解决你的问题吗?

标签: c# .net oop design-patterns architecture


【解决方案1】:

这似乎是一个符合模板模式的案例。您可以使所有实体实现相同的接口/基础并针对该接口执行操作。

我假设唯一需要知道实际类型的部分是Validate()。可以通过两种方式解决:

  1. 让接口/基类声明 Validate,然后在每个具体实体中实现它。
  2. 定义具体实体类型与实际验证策略之间的策略映射。

使用基类抽象验证的示例 -

实体的基础,它具有用于创建的内部服务和用于验证的抽象定义:

public abstract class EntityBase
{
    protected abstract void Validate();

    protected void Create(EntityBase c)
    {
        Log.BeginRequest(c, ActionType.Create);
        c.Validate();
        WebService.Send(Convert(c));
        Log.EndRequest(c, ActionType.Create); 
    }
}

具有验证功能的具体实现者:

public class Customer : EntityBase
{
    private int year;

    public Customer(int year)
    {
        this.year = year;
    }

    public void CreateCustomer(Customer c)
    {
        Create(c);
    }

    protected override void Validate()
    {
        if (year < 1900)
        {
            throw new Exception("Too old");
        }
    }
}

我没有在原始代码中看到 Create 返回的内容,所以我将其更改为 void 以使示例清晰

【讨论】:

  • 你好,以利沙。感谢这个想法,我现在正在实施它。问题:您提到:定义具体实体类型和实际验证策略之间的策略映射。你的意思是类似于: if(c.GetType() == typeof(Customer)) ??另外,您知道这是否会降低性能?谢谢史蒂文
  • 通过映射策略意味着这一点。您也可以使用 if(c is Customer) 来代替类型比较。我不确定性能会有多糟糕,我想这不会太多,因为这里没有严重的反射。您还将为每一次沮丧的人付出代价,铸造成本一点点。虚拟验证的性能会更好。
【解决方案2】:

我认为您正在寻找generic method 解决方案。

public T Create<T>(T t)
{
    Log.BeginRequest(t, ActionType.Create); 
    Validate(t);
    WebService.Send(Convert(t));
    Log.EndRequest(t, ActionType.Create);
    return t;
}

【讨论】:

    【解决方案3】:

    你可以使用泛型方法:

    T Create<T>(T arg)
    

    或者只是 System.Object:

    object Create(object arg);
    {
       Log.BeginRequest(arg, ActionType.Create); 
       Validate(arg);
       WebService.Send(Convert(arg));
       Log.EndRequest(arg, ActionType.Create); 
       return arg; //It must have a return
    }
    
    Car CreateCar(Car c)
    {
       return (Car)Create(c);
    }
    

    【讨论】:

      【解决方案4】:

      如果只是类型不同,您可以提取这十种类型的通用接口,或者您可以创建一个泛型方法。

      【讨论】:

        【解决方案5】:

        是的,具有通用功能:

          public T TrackInstantiation<T>(T entity)
          {
            Log.BeginRequest(entity, ActionType.Create); 
            Validate(entity);
            WebService.Send(Convert(entity));
            Log.EndRequest(entity, ActionType.Create);
            // Don't you also need to return the thing to fulfill the method siugnature ?
            return entity;
          }
        

        我更改了方法的名称,因为您没有在此方法中创建对象,(您正在传递一个已经创建的实例)您只是在验证、持久化和记录它的创建。顺便说一句,为什么不在这里实际创建对象呢?那么这将接近称为抽象工厂的模式。

        您也可以使用界面来做同样的事情。

        public interface ICanBeTracked { /* 没有方法 */ }

        修改你想要传递给这个方法的每个类型,以便它们也补充这个接口,然后写你的方法

          public ICanBeTracked TrackInstantiation(ICanBeTracked  entity)
          {
            Log.BeginRequest(entity, ActionType.Create); 
            Validate(entity);
            WebService.Send(Convert(entity));
            Log.EndRequest(entity, ActionType.Create);
            // Don't you also need to return the thing to fulfill the method siugnature ?
            return entity;
          }
        

        ... 并为上述方法调用的三个方法中的每一个进行重载,输入参数类型为 ICanBeTracked 参考

        【讨论】:

          【解决方案6】:
           public T Create<T>(T c)
           {
              Log.BeginRequest(c, ActionType.Create); 
              Validate(customer);
              WebService.Send(Convert(c));
              Log.EndRequest(c, ActionType.Create); 
           }
          

          【讨论】:

            【解决方案7】:

            我看到了一些关于模板函数的建议。这很好,但它并不能说明整个故事。我猜部分问题是您的每个 Validate 和 Convert 函数的工作方式都略有不同,这意味着一个简单的通用函数可能不够用。

            如果是这种情况,您有几个选择。一种是重载 Validate/Convert 函数,让类型系统的重载决议决定调用哪一个。另一个(这是首选)是使用前面推荐的模板模式,并且只让您的每个类型实现一个公共接口。第三种选择是要求每个方法的函数都有一个委托参数。您已经有了前两个的示例,所以这里有一个代码示例,说明如何编写接受委托的方法:

            public T Create<T>(T c, Action<T> validate, Func<T, string> convert)
            {
                Log.BeginRequest(c, ActionType.Create);
            
                validate(c);
                WebService.Send(convert(c));
            
                Log.EndRequest(c, ActionType.Create);
            }
            

            【讨论】:

              【解决方案8】:

              您可以使用面向方面的编程来应用日志记录和验证。使用 Microsoft Patterns & Practices 团队的 PIAB(策略注入应用程序块)(企业库的一部分)(并假设配置到位),您最终可能会得到如下结果:

              [LogRequest(ActionType.Create)]
              [DoValidate()]
              public T Create<T>(T item)
              {
                 WebService.Send(convert(item));
              }
              

              其中 LogRequest 是一个自定义属性,它实例化一个新的 ICallHandler,其中将记录请求的开始,调用 getNext()(它将控制发送到链中的下一个 ICallHandler,或者如果这是链中的最后一个,将控制权发送给被调用的方法),然后当控制权返回给 ICallHandler 时,它将记录请求的结束。

              验证可能由与 PIAB 无缝协作的验证应用程序块处理 或者您可以编写自己的验证处理程序

              AOP 可以大大减少您需要的代码量,但请注意不要过度使用它。

              您可以在此处阅读 Enterprise Library:http://www.codeplex.com/entlib/

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2011-01-06
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-04-18
                • 2013-08-13
                • 1970-01-01
                相关资源
                最近更新 更多