C#“无法在接口中指定构造函数/静态方法”问题所需的正确解决方案

时间:2011-07-05 03:12:32

标签: c#

好的,我需要一些帮助。

这是旧的“不能使用接口强制构造函数/静态方法”的问题。

什么是合适的设计呢?

我有一组数据实体(Entity Framework的东西),我为此编写了部分类方法来转换为XML(XElement对象)。

我有一个实例方法来“保存”XML:

// Convert entity to XML
public XElement ToXml() {}

...我有一个构造函数来“读取”XML:

// Create entity from XML constructor.
public MyEntity(XElement) {}

或者,我可以使用静态工厂方法来“读取”XML:

public static MyEntity ParseXml(XElement) {}

困境:

  1. 我可以创建一个强制执行“保存”ToXml()方法的界面,但如果它只能解决一半问题呢?接口无法强制执行任何“加载”方法。

  2. 我可以依靠自己的良好意图在没有任何合同的情况下创建这些方法。

  3. 我可以创建一个充满冗余方法的静态类,如XmlToEntity1()XmlToEntity2()等...(现在我已经描述了一个很好的'泛型'问题。)但是,特定的转换代码(特定于每个实体)会为每个实体创建单独的方法或switch / case,并且似乎属于实体类,而不是在其他类中,不是吗?

  4. 如果有经验的C#编码器可以为这个常见问题展示一个好的设计,我想我会从中学到很多东西。

    七月四日快乐!

    可能的解决方案1 ​​

    具有两个静态通用方法的单个XmlSerializer类:

    public static T Deserialize<T>(XElement xml) {}
    public static XElement Serialize<T>(T entity) {}
    
    • Pro:只有一个类(不需要接口)
    • Pro:将序列化责任与实体类分开。
    • Con:对于支持的每种实体类型,仍然需要单独的方法或开关/案例块。
    • 骗局:?不可扩展 - 每次实体更改或添加/删除时都必须修改此类。

    可能的经验教训?

    “不能使用构造函数和静态方法的接口”问题可能是以下症状:

    1. 违反SRP(单一责任委托人)。
    2. 违反SoC(分离关注)主体。

2 个答案:

答案 0 :(得分:5)

使用简单的实例方法从XML加载怎么样?您的界面将类似于:

public interface XmlSerializableEntity
{
   XElement Serialize(); // or ToXml() if you prefer..
   void Deserialize(XElement e); // or Load() or something like that..
}

或者您可以使用通用解决方案:

public interface Serializable<T>
{
   T Serialize();
   void Deserialize(T e);
}

缺点是你必须在加载实体对象之前对其进行初始化,这可能意味着你的对象处于无效状态。但我相信这是一种常见的模式。

答案 1 :(得分:4)

无论如何,将存储/检索与实体分开是一个很好的设计。在OO术语中,这可以称为单一责任原则。您的实体出于某种目的而存在(可能与您的域名相关)。该实体的存储是一项单独的责任,可以独立于域进行更改。 (例如,您可以从数据库,Web服务或文件系统中检索它。)

静态方法当然不是唯一的方法。您可以在保存/检索级别创建接口,并为每个实体实现该接口。然后,您可以轻松地以通用方式使用它们,而无需担心类型。

添加一些示例代码:

interface EntityGateway<TEntity> {
    TEntity Load(String id);
    void Save(TEntity entity);
}
public class XEntityGateway implements EntityGateway<XEntity> {
    XEntity Load(String id) { ... implementation details } 
    void Save(XEntity entity) { ... implementation details } 
}

XEntityGateway gw = new XEntityGateway();
XEntity entity = gw.Load("SOMEID");
// modify entity
gw.Save(entity);