好的,我需要一些帮助。
这是同一个古老的"不能使用接口来强制构造函数/静态方法">问题。
那么什么是正确的OO设计
我有一组数据实体(实体框架的东西(,我为它们编写了部分类方法来转换为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) {}
困境:
我可以创建一个接口;保存";
ToXml()
方法,但如果它只解决了一半的问题,那又有什么好处呢?接口不能强制执行";负载";方法。我可以依靠我自己的良好意愿,在没有任何合同的情况下创建这些方法。
我可以创建一个静态类,其中填充了
XmlToEntity1()
和XmlToEntity2()
等冗余方法。(现在我已经描述了一个很好的"泛型"问题。(然而,特定的转换代码(特定于每个实体(会为每个实体创建单独的方法或切换/case,并且似乎属于实体类内部,而不是其他类中,不是吗?
如果一个经验丰富的C#编码器能够为这个常见问题展示出一个良好的设计,我想我会从中学到很多。
七月四日快乐!
可能的解决方案1
具有两个静态泛型方法的单个XmlSerializer
类:
public static T Deserialize<T>(XElement xml) {}
public static XElement Serialize<T>(T entity) {}
- Pro:只有一个类(不需要接口(
- Pro:将序列化责任与实体类分离
- Con:对于支持的每个实体类型,仍然需要单独的方法或开关/用例块
- Con:?不可扩展——每次实体更改或添加/删除时都必须修改此类
可能吸取的教训
";不能为构造函数和静态方法使用接口&;问题可能是的症状
- 违反SRP(单一责任负责人(
- 违反SoC(关注分离(原则
使用一个简单的实例方法从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);
}
缺点是在加载实体对象之前必须对其进行初始化,这可能意味着对象将处于无效状态。但我相信这是一种常见的模式。
无论如何,将存储/检索与实体分离是一个很好的设计。在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);