具有多个参数的类构造函数

时间:2009-04-05 14:34:26

标签: c#

调用可以包含大量参数的类构造函数的最佳方法是什么?

例如,如果我想开发一个组件来自动记录应用程序中的异常;我们称之为'ExceptionLogger'

ExceptionLogger有3种方法可以编写引用它的应用程序生成的错误。

ToLogFile              (takes 2 parameters)
ToDatabase             (takes 2 parameters)
ToEmail                (take 4 parameters)

这3个方法中的每一个都是ExceptionLogger专用的,调用应用程序需要通过类构造器“打开”这些方法;如果需要,还提供参数。

调用应用程序只需使用'publish'方法让ExceptionLogger将信息写入相关存储。

添加澄清;我的意图是让一个ExceptionLogger实例能够进行多次写入

6 个答案:

答案 0 :(得分:3)

似乎这可能是使用继承的好地方。你可以拥有一个FileLogger,DatabaseLogger和EmailLogger,每个都来自一个基本的ExceptionLogger类,并且只有一个合适的构造函数。

答案 1 :(得分:2)

您可能需要考虑在层次结构中使用Named Constructor范例或三个单独的类。听起来你有三个不同的类,每个类都有自己的构造函数要求。如果构造函数参数中的数据是实例操作所必需的,那么它们必须是构造函数的参数,否则最终会得到一个缺少数据的实例。

另一种方法是使用Named Parameters来表示真正的可选参数。我相信boost也提供了一个实现命名参数的框架。

答案 2 :(得分:1)

澄清,我读了你的问题,并假设一个ExceptionLogger实例可以通过多种类型的通信进行写入。

对于这个特定的例子,我将封装所需的参数,以便将三种写入方法中的每一种都放入一个单独的类中。说

  • LogFileConstructionInfo
  • DatabaseConstructionInfo
  • EmailConstructionInfo
然后我会创建4个不同的构造函数。一种用于上述每种类型仅接受该类型。这样可以快速轻松地创建只以单一方式记录的ExceptionLogger实例。它还使得callsite代码非常清楚它正在使用哪种方法。

为了允许多种写入方法,我将定义第四个构造,它具有三个参数,每个参数对应于上述类型。参数允许Null或其他一些缺少值选项(如选项)。这将允许创建任何作者组合。

答案 3 :(得分:0)

使用能够将数据写入日志文件/数据库/电子邮件的单独类,并传递要在构造函数中使用的类。 并使它们都实现相同的接口

示例:

LogDatabaseWriter writer = new LogDatabaseWriter(param1, param2, param3);
Logger log = new Logger(writer);

编辑:更多代码

所以你有一个示例界面:

interface ILogWriter
{
    public void Write(string s);
}

接口的几个实现

class LogDatabaseWriter : ILogWriter
{
    //constructor
    // ...

    //implement the required interface methods
    public void Write(string s)
    {
        //Do your thing
    }
}

你的Logger类有一个像这样的构造函数:

class Logger
{
    private ILogWriter _writer;

    public Logger(ILogWriter writer)
    { 
        _writer = writer;
        //Do your thing 
    }
}

答案 4 :(得分:0)

如果不应该扩展存储类型,我建议使用enum,就像这样

public enum StorageLocation
{
   None,
   File,
   Database,
   Email      
}

public class ExceptionLogger
{
   private StorageLocation m_Storage;       

   public StorageLocation Storage
   {
      get { return m_Storage; }
      set { m_Storage = value; }
   }

   // ...
}

如果您认为将来需要扩展存储位置,那么我将创建一个接口IStorageLocation并使用它来获取/设置ExceptionLogger中的Storage属性。会有更多的努力,但你会获得更大的灵活性。

答案 5 :(得分:0)

  1. 写几个不同论点的ctors。

  2. 使ctor成为私有或受保护的,并添加三个不同名称的静态函数(例如,电子邮件,记录器,数据库),这些函数采用正确的args并将它们传递给ctor,以及有关哪种EceptionLogger的信息构造,然后返回新构造的对象。这是命名构造函数Pattern。 http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.8

  3. 作为1,但传入一个伪参数来区分本来会有相同签名的ctors。一个有用的虚拟对象是类中定义的标记接口。 标记接口是一种类型,除了通过传递信号之外什么都不做 或用作基类。 e.g。

    public class ExceptionLogger {
      private class AsEmail {} ;
      public static AsEmail asEmail;
      private class AsLogFile {} ;
      public static AsLogFile asLogFile {};
      ExceptionLogger( const AsEmail&, const char* const ) ;
      ExceptionLogger( const AsLogFile&, const char* const ) ;
  4. 作为3,但是通过枚举区分ctors,并在ctor内分支。 (这个很难看。)

  5. 通过返回* this,“Named Parameter Idiom”将setter添加到“chain”类中: http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.18鉴于你有一个参数的脱节,而不是它们的联合,三个可能是ExceptionLogger的错误选择。

  6. 传入另一个包含参数的类,一个Traits类。

  7. 保持您班级中的通用逻辑,将变体逻辑作为GOF AbstractStrategy的(单个)GOF ConcreetStrategy sublcass传递。类似于3,但现在逻辑在论证中。

  8. 保持您的类中的公共逻辑,但使其成为三个不同子类的基类,这些子类将各种策略分开,然后调用基类中的常用功能。每个子类都有一个ctor,但它们可以具有相同的参数,因为ctors不是继承的。

  9. 保持您的类中的通用逻辑,但是从三个不相关的类委托它,每个类通过包含或作为私有基类来保存它。同样,每个人都可以拥有自己的ctor。不好,因为您可能希望ExceptionLoggers具有相同(超级)类型。