C# 类类型的参数字符串+;身份证件
此处的简化报告示例: 假设我有三个不同的值对象,它们都具有ID属性:C# 类类型的参数字符串+;身份证件,c#,asp.net-mvc,oop,C#,Asp.net Mvc,Oop,此处的简化报告示例: 假设我有三个不同的值对象,它们都具有ID属性: public class ObjectA() { int ID { get; set; } } public class ObjectB() { int ID { get; set; } } public class ObjectC() { int ID { get; set; } } 假设我想使用URL中的“ClassTypePlusID”参数,如下所示: http://www.example.com/show/A1 ht
public class ObjectA() { int ID { get; set; } }
public class ObjectB() { int ID { get; set; } }
public class ObjectC() { int ID { get; set; } }
假设我想使用URL中的“ClassTypePlusID”参数,如下所示:
http://www.example.com/show/A1
http://www.example.com/show/B3
http://www.example.com/show/C2
现在,在我的视图模型中,提取类型(A、B或C)和ID(1、3或2)非常容易。因此,创建一个值对象并不困难(简化,无错误处理):
然后,我有三个GetData()方法来获取与对象相关的数据(这里再次大大简化):
最后,我们找到了我痛苦的根源。。。现在我需要将该值对象提供给一个方法。但我必须(重新)将对象转换为正确的类型,然后才能将其用作参数。我不能这样做:
var valueObject = GetValueObject(urlParameter);
GetData(valueObject);
public class ObjectA() : IObject
编译器(我想这是正确的)无法从object转换为ObjectA、ObjectB或ObjectC。所以我不得不做这个可笑的扭曲动作:
var valueObject = GetValueObject("A1");
if (valueObject is ObjectA)
{
GetData((ValueObjectA)valueObject);
}
else if (valueObject is ObjectB)
{
GetData((ValueObjectB)valueObject);
}
else if (valueObject is ObjectC)
{
GetData((ValueObjectC)valueObject);
}
雪上加霜的是,我还没有完成if语句,因为我必须在视图中再次使用它们才能显示数据对象(无论是哪一个)
因此,我一直在追根究底,试图弄清楚如何在没有所有丑陋(难以维护)的if语句的情况下做到这一点。必须有一种面向对象的方法来做到这一点,但我正在努力弄清楚它是什么
EDIT:OK,如我所想,第一个建议是使用继承。让我试试,也许你能告诉我我错过了什么。
我确实了解继承和多态性的基础知识,但我仍在学习如何有效地使用它们(如您所见)
因此,如果我要使用继承,我需要有一个抽象类,该类在要进行继承的对象之间具有公共属性/行为。我看不出有任何共同之处。我在示例中展示的GetData
方法将利用特定于对象的存储库,并在ViewModel中返回(或存储)DTO,以便视图可以访问它。因此,该方法的输入不常见(它将是三个值对象之一),而该方法的输出也不常见(它将是包含相应表中数据的三个不同DTO之一)
我不是说继承或多态性不是答案(我肯定是的),我只是不知道如何利用它
编辑#2:在我试图简化这个问题时,我可能走得太远了。为移动的目标道歉,第一次没有正确表达我的问题,这不是我的意图
在最初的问题中,我试图推断ObjectA、B和C是实际值对象(每个DDD),但我从来没有明确地说出来。所以我现在说,我更新了示例代码,希望能更好地加强这一点
将GetData
方法移动到Value对象中会出现一些问题。首先,我认为它不属于那里。ValueObject的用途是描述特定类型的ID。它在我的域中的多个实体中使用。另一个问题是,在我的示例中,GetData
方法被简化了(可能太多了),它实际上有附加的ID参数(也从URL中提取)。从这里开始,GetData
方法将利用各种存储库(来自多个数据集)用所需的数据填充视图模型。所以这里的底线是我在这里的应用服务层。我不相信GetData
方法属于域层中的值对象内部
希望这能更好地澄清这个问题(但我觉得我肯定可以做得更好,如果我有什么想法,我会继续思考并更新)。为什么你不能有一个界面,你可以坚持,然后有一个单一的功能,并删除所有的分支。。。此外,int.parse和子字符串都是肤浅的,分配这样小的短期堆对象是很糟糕的做法。。。您只需要Id为字符串[0]和字符串[1]的字符。要扩展Jay的答案,以下是如何实现: 该接口理想地定义了其他不相关类之间的所有通用方法和属性。您已经实现了此接口,因此只需添加如下接口:
var valueObject = GetValueObject(urlParameter);
GetData(valueObject);
public class ObjectA() : IObject
为了使这个接口真正有用,我建议将GetData
方法移动到它们的相关类中。这样,您就可以修改界面以包含该方法:
public interface IObject
{
int ID { get; set; }
void GetData();
}
完成此操作后,您可以更改getValueObject
方法的签名以返回接口:
public IObject GetValueObject(string ClassTypePlusID)
然后,不必检查对象的类型,只需调用其GetData方法:
var valueObject = GetValueObject("A1"); //var is of type IObject
ObjectA.GetData();
下面的方法行吗 我不清楚GetData()方法应该是由对象完成的还是在实例化它们的类上完成的。如果要避免If-else块,则需要预先知道它们的共同属性,以便声明适当的接口,或者将获取数据的责任委托给对象本身(正如我在下面通过实现IDataProvider接口所做的那样) 我使用了一个字典在各种字符串和类型本身之间映射(只做了a和B,但您可以扩展它)。我已经使用泛型来解决您的类型转换问题,但是您甚至可能不需要知道类型,因为对于调用方类来说,重要的是它可以获取数据(),而不是类是什么。我已经在c的测试用例中展示了这一点,它可以同样容纳ObjectB的一个实例
public interface IDataProvider
{
void GetData();
}
public class ObjectA : IDataProvider
{
public ObjectA(int id)
{
this.Id = id;
}
public int Id
{
get;
}
public void GetData()
{
// Get A's data
}
}
public class ObjectB : IDataProvider
{
public ObjectB(int id)
{
this.Id = id;
}
public int Id
{
get;
}
public void GetData()
{
// Get B's data
}
}
public static class ObjectFactory
{
private static readonly Dictionary<string, Type> typeByNameDictionary = new Dictionary<string, Type>();
static ObjectFactory()
{
typeByNameDictionary.Add("A", typeof(ObjectA));
typeByNameDictionary.Add("B", typeof(ObjectB));
}
public static bool TryGetObject<T>(string classTypePlusId, out T createdObject) where T : class
{
string objectName = classTypePlusId.Substring(0, 1);
if (!int.TryParse(classTypePlusId.Substring(1, 1), out int id))
{
throw new ArgumentOutOfRangeException(classTypePlusId, "something meaningful");
}
if (!typeByNameDictionary.TryGetValue(objectName, out Type objectType))
{
createdObject = default(T);
return false;
}
createdObject = Activator.CreateInstance(objectType, id) as T;
return createdObject != null;
}
}
[TestFixture]
public class Test
{
[Test]
public void CanCreateObjects()
{
ObjectA a;
ObjectB b;
IDataProvider c;
Assert.That(ObjectFactory.TryGetObject("A1", out a), Is.True);
Assert.That(a.Id, Is.EqualTo(1));
a.GetData();
Assert.That(ObjectFactory.TryGetObject("A1", out b), Is.False);
Assert.That(ObjectFactory.TryGetObject("B4", out b), Is.True);
Assert.That(b.Id, Is.EqualTo(4));
b.GetData();
Assert.That(ObjectFactory.TryGetObject("A2", out c), Is.True);
c.GetData();
}
}
然后可以声明ObjectA、ObjectB和ObjectC以支持这些。这样,服务的GetData方法只接受IObjectWithCommonProperties参数。(请挑一个
public interface IDataProvider
{
void GetData();
}
public class ObjectA : IDataProvider
{
public ObjectA(int id)
{
this.Id = id;
}
public int Id
{
get;
}
public void GetData()
{
// Get A's data
}
}
public class ObjectB : IDataProvider
{
public ObjectB(int id)
{
this.Id = id;
}
public int Id
{
get;
}
public void GetData()
{
// Get B's data
}
}
public static class ObjectFactory
{
private static readonly Dictionary<string, Type> typeByNameDictionary = new Dictionary<string, Type>();
static ObjectFactory()
{
typeByNameDictionary.Add("A", typeof(ObjectA));
typeByNameDictionary.Add("B", typeof(ObjectB));
}
public static bool TryGetObject<T>(string classTypePlusId, out T createdObject) where T : class
{
string objectName = classTypePlusId.Substring(0, 1);
if (!int.TryParse(classTypePlusId.Substring(1, 1), out int id))
{
throw new ArgumentOutOfRangeException(classTypePlusId, "something meaningful");
}
if (!typeByNameDictionary.TryGetValue(objectName, out Type objectType))
{
createdObject = default(T);
return false;
}
createdObject = Activator.CreateInstance(objectType, id) as T;
return createdObject != null;
}
}
[TestFixture]
public class Test
{
[Test]
public void CanCreateObjects()
{
ObjectA a;
ObjectB b;
IDataProvider c;
Assert.That(ObjectFactory.TryGetObject("A1", out a), Is.True);
Assert.That(a.Id, Is.EqualTo(1));
a.GetData();
Assert.That(ObjectFactory.TryGetObject("A1", out b), Is.False);
Assert.That(ObjectFactory.TryGetObject("B4", out b), Is.True);
Assert.That(b.Id, Is.EqualTo(4));
b.GetData();
Assert.That(ObjectFactory.TryGetObject("A2", out c), Is.True);
c.GetData();
}
}
public interface IObjectWithCommonProperties
{
int Id
{
get;
}
string StoredProcName
{
get;
}
bool IsSomeOtherFact
{
get;
}
// etc
}
public interface IObjectBuilder
{
object GetCreateObjectWithData(string classPlusId);
}
public class ObjectABuilder : IObjectBuilder
{
public object GetCreateObjectWithData(string classPlusId)
{
ObjectA loadedObject;
ObjectFactory.TryGetObject(classPlusId, out loadedObject);
loadedObject.SomeProperty = "someValue"; // etc
return loadedObject;
}
}