我的抽象目标是:
1)创建一个CRUD API,它是带有CRUD接口的第三方库的包装器(可以来自服务对象和/或实体对象)
2)每个CRUD方法都应该根据预定义的类型定义限制输入的类型(一种类型可以提供给多种方法)
3)当User选择特定类型的方法时,他应该被迫根据所选类型插入正确类型的其他参数(如键),并且应该在编译时验证(将键作为对象传递) type要求对象的“真实”类型进行运行时评估。
4)方法的类型是第三方接口,不在我的控制范围内,我无法改变它们。
5)对于用户来说,API应该是直截了当的,而且他的样板代码量较少。
我发现在C#中解决此问题的一种方法是:
public interface Update<T,TKey> {}
public interface Add<T> {}
public interface Delete<T,TKey> {}
public interface Get<T,TKey> {}
public class Invoice:Get<string>, Add<ThirdPartyInvoice>, Update<ThirdPartyInvoice,string> {}
//More types can come here...
public static class CRUDAPI
{
public static T Get<T,TKey>(Get<T,TKey> type, TKey key)
{
//will get a T from a service object based on TKey
}
public static Unit Add<T>(Add<T> type, Func<T,T> select)
{
//will get a new instance of T and will feed it to the select function.
//and then will feed the result to the 3rd party add method
}
public static Unit Update<T,TKey>(Update<T,TKey> type,TKey key, Func<T,T> select)
{
//will load an instance of T and will feed it to the select function.
//and then will feed the result to the 3rd party update method
}
public static Unit Delete<T,TKey>(Delete<T,TKey> type,TKey key)
{
//will load an instance of T and then will use 3rd party delete method
}
}
用户可以像以下一样使用它:
Add(new Invoice(), inv=> { inv.field1 = "123"; ... return inv;})
在功能样式中解决此问题的好方法是什么(例如在F#中)?
答案 0 :(得分:1)
在函数式编程中,您可以使用泛型 http://app2.dev/解决该问题。通常,您会决定处理“命令”原语,例如:
data Command a b c = Command(a -> Either b c)
由您决定在命令的特殊性,通用参数的数量和通用(输入-输出)接口之间找到合适的平衡:尽管algebraic types是经典选择,也可以在F#中使用;另一种常用的类型是Either,也由F#提供。
然后您可以定义:
data CRUD a b c d e f = CRUD{ create :: Command a b c, read :: Command d e f, ... }
再次找到合适的余额。
然后您发现,有很多命令。真。其中一些直接进入数据库,其他负责DTO->模型映射,其他负责验证。您想将它们组合成乐高时尚的Command a b c -> Command b e d -> Command a e d
,可以通过扩展Command
的代数类型... | CompositeCommand(a -> Either b c, b -> Either e d)
来解决,该类型在内部将依赖于{{1} }的单链绑定以链接这两个功能。