Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/144.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 采用对象参数还是使用成员对象更好?_C++_Oop - Fatal编程技术网

C++ 采用对象参数还是使用成员对象更好?

C++ 采用对象参数还是使用成员对象更好?,c++,oop,C++,Oop,我有一个类,我可以这样写: class FileNameLoader { public: virtual bool LoadFileNames(PluginLoader&) = 0; virtual ~FileNameLoader(){} }; 或者这个: class FileNameLoader { public: virtual bool LoadFileNames(PluginLoader&, Lo

我有一个类,我可以这样写:

class FileNameLoader
{
     public:
         virtual bool LoadFileNames(PluginLoader&) = 0;
         virtual ~FileNameLoader(){}
};
或者这个:

class FileNameLoader
{
     public:
         virtual bool LoadFileNames(PluginLoader&, Logger&) = 0;
         virtual ~FileNameLoader(){}
};
第一个假设在
FileNameLoader
的实现中有一个成员
Logger&
。第二个没有。但是,我有一些类,它们有许多内部使用
Logger
的方法。因此,第二种方法将使我在这种情况下编写更多的代码<代码>记录器目前是一个单身汉。我的猜测是,它将保持这种方式。二者中哪一个更“美丽”,为什么?通常的做法是什么

编辑:
如果该类未命名为
记录器
?:),该怎么办。我还有一个
Builder
。那么呢?

我更喜欢第二种方法,因为它允许更健壮的黑盒测试。此外,它还使函数的界面更清晰(事实上它使用了这样一个Logger对象)。

我看不出方法2比方法1有什么额外的优势(甚至考虑到单元测试!),事实上,对于方法2,您必须确保无论在何处调用特定的方法,一个记录器可以传递进来,这会使事情变得复杂


一旦你用记录器构造了一个对象,你真的认为有必要改变它吗?如果不是,为什么还要麻烦使用方法二呢?

我最喜欢的日志记录方法是在我的类中有一个
Logger
类型的成员(不是引用或指针,而是实际对象)。
根据日志记录基础设施的不同,可以根据每个类来决定输出应该放在哪里或使用哪个前缀


与第二种方法相比,这种方法的优点是不会(意外地)造成在日志文件中无法轻松识别同一类成员的情况。

一般来说,我认为参数越少,功能越好。通常情况下,函数的参数越多,函数就越趋于“通用”——这反过来会导致大型复杂函数尝试执行所有操作

假设Logger接口用于跟踪,在这种情况下,我怀疑FileNameLoader类的用户是否真的希望提供应该使用的特定日志实例

您还可以应用Demeter定律作为反对在函数调用中提供日志实例的参数

当然,在某些特定的时间,这是不合适的。一般的例子可能是:

  • 对于性能(仅应在确定具体性能问题后进行)
  • 为了帮助通过模拟对象进行测试(在这种情况下,我认为构造函数是一个更合适的位置,因为记录剩余的单例可能是一个更好的选择…)

我会坚持使用第一种方法,并将记录器作为单例使用。不同的接收器和识别数据从何处记录是完全不同的问题。识别水槽可以是简单的,也可以是复杂的。例如(假设Singleton是代码中Singleton的基类):


您可以拥有一个固有的复杂日志管理器,它具有不同的接收器、不同的日志级别和不同的输出。日志管理器上的Log()方法应该支持如上所述的简单日志记录,然后可以考虑更复杂的示例。例如,出于调试目的,您可以为不同的接收器定义不同的输出,并拥有一个组合日志。

第一件事是确保用户在这两种情况下都提供了记录器依赖项。大概在第一种情况下,FileNameLoader的构造函数接受一个
记录器&
参数

在任何情况下,我都不会让记录器成为单身汉。从来没有,从来没有,没有办法,没有办法。它要么是一个注入依赖项,要么有一个
Log
自由函数,或者如果您绝对必须使用
std::ostream
对象的全局引用作为通用默认记录器。单例记录器类是一种为测试设置障碍的方法,绝对没有实际的好处。那么,如果某个程序确实创建了两个记录器对象呢?为什么这甚至很糟糕,更不用说为自己制造麻烦来预防了?在任何复杂的日志系统中,我发现自己首先要做的事情之一是创建一个Prefixloger,它实现了日志接口,但在所有消息的开头打印一个指定的字符串,以显示一些上下文。Singleton与这种动态灵活性不兼容

然后,第二件事是询问用户是否想要一个FileNameLoader,并多次调用LoadFileNames,第一次调用一个记录器,第二次调用另一个记录器

如果是这样的话,那么您肯定希望函数调用中有一个Logger参数,因为更改当前Logger的访问器(a)不是一个很好的API,(b)无论如何都不可能使用引用成员:您必须更改为指针。您也许可以将记录器参数设置为默认值为
0
的指针,但
0
表示“使用成员变量”。这将允许用户在初始设置代码知道并关心日志记录的情况下使用,但随后该代码将FileNameLoader对象交给将调用LoadFileNames但不知道或关心日志记录的其他代码

如果不是,则Logger依赖项对于类的每个实例都是不变的,使用成员变量也可以。我总是稍微担心引用成员变量,但原因与此选择无关

[关于构建器的编辑:我认为您可以在我的回答中进行搜索和替换,但它仍然有效。关键区别在于“此FileNameLoader对象使用的构建器”是否对给定对象是不变的,或者“调用中使用的构建器”是否是调用方在每次调用bas时需要配置的内容
class Logger : public Singleton<Logger>
{
public:
    void Log(const std::string& _sink, const std::string& _data);
};
class FileNameLoader
{
 public:
     virtual bool LoadFileNames(PluginLoader& _pluginLoader)
     {
         Logger.getSingleton().Log("FileNameLoader", "loading xyz");
     };

     virtual ~FileNameLoader(){}
};