将基类型转换为派生类型

时间:2010-08-13 13:43:37

标签: c# types

基类是Task。有几个派生类,如PhoneCall,传真,电子邮件.... 该框架是.NET 3.5,语言是C#

在我们的应用程序中,我们希望根据客户的某些规则创建一些自动任务。例如。如果客户已注册30天,则任务将由规则引擎创建。

然后,任务的所有者应该能够根据场景将此任务转换为PhoneCall,Fax .....此外,另一个要求是将PhoneCall转换为传真或电子邮件,反之亦然。

1)是否应该有一个转换类来促进这种转换,或者每个业务对象应该允许方法执行转换?

2)如果有人可以提供任何设计模式或指导,那就太棒了。

PRATIK

3 个答案:

答案 0 :(得分:4)

继承不一定是对类型实例可能随时间发生变化的问题进行建模的最佳方式。

您可能需要考虑使用合成。类似的东西:

class Task
{
    private TaskDetail m_Detail;

    public TaskDetail Detail { get { return m_Detail; } }
}

abstract class TaskDetail { ... }

class PhoneCallDetail : TaskDetail { ... }
class FaxDetail : TaskDetail { ... }
class EmailDetail : TaskDetail { ... }

当任务细节从一种类型转移到另一种类型时,任务不会改变。您还需要实现一些实用程序代码,以便在不同的任务类型之间进行转换。

所以示例使用可能如下:

Task theTask = new Task( ... );
theTask.ConvertToEmail(); // internally establishes this as an email task

EmailDetail detail = (EmailDetail)theTask.Detail;
detail.EmailAddress = "wiley.coyote@acme.com";

theTask.ConvertToFax();   // may transfer or lose some detail...
FaxDetail faxDetail = (FaxDetail)theTask.Detail;
faxDetail.FaxDate = DateTime.Now;

// and so on.

上述方法的主要缺点是Task类的使用者必须使用运行时检查来确定与任务相关的详细信息类型,然后再对其进行操作;这也需要在任何地方投射细节属性:

Task someTask = ...;
if( someTask.Detail is EmailDetail )
{ 
    EmailDetail detail = (EmailDetail)someTask.Detail;
    /* operate on email detail ... */
}
else if( someTask.Detail is FaxDetail )
{
    FaxDetail detail = (FaxDetail)someTask.Detail;
    /* operate on fax detail ... */
}

随着不同亚型数量的增加,这种方法变得难以维持和发展。如果子类型的数量很少,并且随着时间的推移可能会保持稳定,那么这可能是一个合理的选择。

通常,很难对这些情况进行建模 - 而且您经常必须根据您使用的持久性提供程序,它们有多少不同的详细信息类型以及您打算支持哪些用例涉及来自一个详细信息类型的转换来妥协到另一个。

在这种情况下经常使用的另一种设计方法是键值编码这个approch使用键/值字典来模拟各种数据元素。不同种类的细节。这使得细节非常灵活,但代价是编译时安全性降低。我尽可能避免使用这种方法,但有时它会更好地模拟某些问题域。

实际上,可以将键值编码与更强类型的方法结合起来。这允许细节公开其属性(通常用于只读目的),而不需要调用者执行运行时检查或强制转换:

abstract class TaskDetail
{
    public abstract object this[string key] { get; }
}

public class FaxDetail : TaskDetail
{
    public string FaxNumber { get; set; }
    public DateTime DateSent  { get; set; }

    public override object this[string key]
    {
        get
        {
            switch( key )
            {
                case "FaxNumber": return FaxNumber;
                case "DateSent":  return DateSent;
                default:          return null;
            }
        }
    }
}

public class EmailDetail : TaskDetail
{
    public string EmailAddress { get; set; }
    public DateTime DateSent { get; set; }

    public override object this[string key]
    {
       get
       {
           switch( key )
           {
               case "EmailAddress": return EmailAddress;
               case "DateSent":     return DateSent;
               default:             return null;
           } 
       }
    }
}

// now we can operate against TaskDetails using a KVC approach:
Task someTask;
object dateSent = someTask.Detail["DateSent"]; // both fax/email have a DateSent
if( dateSent != null )
   // ...

答案 1 :(得分:0)

1)创建此类任务您可以使用工厂方法

2)我不认为所有者必须将任务转换为子类是个好主意。战略或州政府可能会帮助那里。

答案 2 :(得分:0)

在您的特定情况下,类型不是可行的方法,因为您尚未定义PhoneCall,传真或超出任务的电子邮件的行为。

如果您要做的唯一事情是将电话号码存储在PhoneCall对象中,那么您已经破坏了封装。 PhoneNumber属性应该在Customer对象上,电子邮件地址,邮寄地址,传真号码也应如此。

您可以愉快地将TaskType枚举用作任务 的属性,直到Task根据当前任务类型 更改其行为。这就是你需要担心使用类型转换的方式,就像LBushkin所描述的那样。

在您为每项任务使用不同的演示逻辑的情况下,我可以看到您。在这种情况下,您将使用Decorator模式。