我很难确定好的头衔,所以如有必要,请随时更改。我不太确定如何描述我想要实现的目标,并且想到了“模板”这个词(显然我不是想使用C ++模板)。
如果我有一个在每个方法中都执行某些操作的类,那么让我们假装一个try / catch和其他一些东西:
public class SomeService
{
public bool Create(Entity entity)
{
try
{
this.repository.Add(entity);
this.repository.Save();
return true;
}
catch (Exception e)
{
return false;
}
}
}
然后我添加另一种方法:
public bool Delete(Entity entity)
{
try
{
this.repository.Remove(entity);
this.repository.Save();
return true;
}
catch (Exception e)
{
return false;
}
}
这里的方法显然有一种模式:try / catch返回值。所以我在想,因为服务上的所有方法都需要实现这种工作模式,我可以将它重构成这样的东西:
public class SomeService
{
public bool Delete(Entity entity)
{
return this.ServiceRequest(() =>
{
this.repository.Remove(entity);
this.repository.Save();
});
}
public bool Create(Entity entity)
{
return this.ServiceRequest(() =>
{
this.repository.Add(entity);
this.repository.Save();
});
}
protected bool ServiceRequest(Action action)
{
try
{
action();
return true;
}
catch (Exception e)
{
return false;
}
}
}
这样,所有方法都遵循相同的“模板”来执行。这是一个糟糕的设计吗?请记住,try / catch并不是每种方法都可能发生的。考虑添加验证,需要在每个方法中说if(!this.Validate(entity))...
。
难以维护/丑陋/糟糕的设计吗?
答案 0 :(得分:2)
使用lambda表达式通常会降低可读性。这基本上意味着在几个月内有人会读到这个并且头疼。
如果没有必要,或者没有真正的性能优势,只需使用2个独立的功能即可。 IMO最好有可读的代码然后使用漂亮的技术。
答案 1 :(得分:2)
这似乎是一种仅限于小“行动”的技术 - “行动”中的代码越多,可用性就越小,因为可读性会越来越受到损害。事实上,你在这里真正重用的唯一东西就是try / catch块,它首先可能是糟糕的设计。
这并不是说它一定是一个糟糕的设计模式,只是你的例子似乎并不适合它。例如,LINQ广泛使用这种模式。结合扩展方法和流畅的风格,它可以非常方便,仍然可读。尽管如此,它似乎最适合替换小的“动作” - 除了几行代码之外,我认为它变得非常混乱。
如果您打算这样做,您可能希望通过将操作和实体作为参数而不仅仅是操作传递给操作和实体来使其更有用。这样就更有可能在行动中进行额外的常见计算。
public bool Delete( Entity entity )
{
return this.ServiceRequest( e => {
this.repository.Remove( e );
this.repository.Save();
}, entity );
}
protected bool ServiceRequest( Action<Entity> action, Entity entity )
{
try
{
this.Validate( entity );
action( entity );
return true;
}
catch (SqlException) // only catch specific exceptions
{
return false;
}
}
答案 2 :(得分:1)
我会尝试寻找一种方法来破坏存储库操作(添加/更新/删除)从存储库更改flush(保存)。 根据您使用代码的方式(web / windows),您可以使用“会话”管理器。进行此分离还允许您在单个事务中刷新多个操作。
其他认为,与主题无关但与代码无关,不返回true / false,但允许异常传递或返回允许您区分原因或失败(验证等)的内容。您可能希望抛弃合同违规(传递无效数据)并返回正常无效业务规则的值(不要将异常用作业务规则,因为它们很慢)。
答案 3 :(得分:0)
另一种方法是创建一个界面,比如IExample,它表达了你的更多意图并将动作与actor分离。所以在你最后的例子中你提到也许使用this.Validate(实体)。显然,这不适用于您当前的设计,因为您必须传递操作和实体,然后将实体传递给操作。
如果您将其表达为实体上的接口,则只需传递实现IExample的任何实体。
public interface IExample { bool Validate(); void Action(); }
现在实体实现IExample,ServiceRequest现在将IExample作为参数,bob是你的叔叔。
你的原始设计一点也不差,实际上它很常见,但随着需求的变化而变得有限(这次动作必须被调用两次,这次需要调用验证然后进行验证)。通过接口表达它,使其可测试,并且可以将模式移动到设计用于重放此特定序列的辅助类。一旦提取,它也变得可测试。这也意味着如果需求突然需要调用验证,您可以重新访问整个设计。
最后,如果模式没有被大量应用,例如你在类中可能有三个位置,那么它可能不值得努力,只需编写每个长手的代码。有趣的是,由于事物是Jitted的方式,它可能会更快地拥有三个不同且完整的方法而不是三个共享共同核心...