调用可以包含大量参数的类构造函数的最佳方法是什么?
例如,如果我想开发一个组件来自动记录应用程序中的异常;我们称之为'ExceptionLogger'
ExceptionLogger有3种方法可以编写引用它的应用程序生成的错误。
ToLogFile (takes 2 parameters)
ToDatabase (takes 2 parameters)
ToEmail (take 4 parameters)
这3个方法中的每一个都是ExceptionLogger专用的,调用应用程序需要通过类构造器“打开”这些方法;如果需要,还提供参数。
调用应用程序只需使用'publish'方法让ExceptionLogger将信息写入相关存储。
添加澄清;我的意图是让一个ExceptionLogger实例能够进行多次写入
答案 0 :(得分:3)
似乎这可能是使用继承的好地方。你可以拥有一个FileLogger,DatabaseLogger和EmailLogger,每个都来自一个基本的ExceptionLogger类,并且只有一个合适的构造函数。
答案 1 :(得分:2)
您可能需要考虑在层次结构中使用Named Constructor范例或三个单独的类。听起来你有三个不同的类,每个类都有自己的构造函数要求。如果构造函数参数中的数据是实例操作所必需的,那么它们必须是构造函数的参数,否则最终会得到一个缺少数据的实例。
另一种方法是使用Named Parameters来表示真正的可选参数。我相信boost也提供了一个实现命名参数的框架。
答案 2 :(得分:1)
澄清,我读了你的问题,并假设一个ExceptionLogger实例可以通过多种类型的通信进行写入。
对于这个特定的例子,我将封装所需的参数,以便将三种写入方法中的每一种都放入一个单独的类中。说
为了允许多种写入方法,我将定义第四个构造,它具有三个参数,每个参数对应于上述类型。参数允许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)
写几个不同论点的ctors。
使ctor成为私有或受保护的,并添加三个不同名称的静态函数(例如,电子邮件,记录器,数据库),这些函数采用正确的args并将它们传递给ctor,以及有关哪种EceptionLogger的信息构造,然后返回新构造的对象。这是命名构造函数Pattern。 http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.8
作为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 ) ;
作为3,但是通过枚举区分ctors,并在ctor内分支。 (这个很难看。)
通过返回* this,“Named Parameter Idiom”将setter添加到“chain”类中: http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.18鉴于你有一个参数的脱节,而不是它们的联合,三个可能是ExceptionLogger的错误选择。
传入另一个包含参数的类,一个Traits类。
保持您班级中的通用逻辑,将变体逻辑作为GOF AbstractStrategy的(单个)GOF ConcreetStrategy sublcass传递。类似于3,但现在逻辑在论证中。
保持您的类中的公共逻辑,但使其成为三个不同子类的基类,这些子类将各种策略分开,然后调用基类中的常用功能。每个子类都有一个ctor,但它们可以具有相同的参数,因为ctors不是继承的。
保持您的类中的通用逻辑,但是从三个不相关的类委托它,每个类通过包含或作为私有基类来保存它。同样,每个人都可以拥有自己的ctor。不好,因为您可能希望ExceptionLoggers具有相同(超级)类型。