Oop 我应该如何构造这个类继承?

Oop 我应该如何构造这个类继承?,oop,inheritance,coldfusion,Oop,Inheritance,Coldfusion,我正在开发一个应用程序,用于将用户上传的图像/文档的数据库和目录从生产服务器传输到开发服务器。应用程序是用Coldfusion编写的,但我认为该语言与这个问题无关——它更多地是一个结构/架构问题,而不是该语言特有的问题 我将为我的示例编写伪代码,所以请不要对语法进行区分 当我看到类继承的演示时,它通常是一些简单的东西,比如类Student extensed Person(){}。显然,学生是一个更专业的人,所以这是有道理的 在我的应用程序中,我有一个类Site(),其中包含相关信息,如DSN、文

我正在开发一个应用程序,用于将用户上传的图像/文档的数据库和目录从生产服务器传输到开发服务器。应用程序是用Coldfusion编写的,但我认为该语言与这个问题无关——它更多地是一个结构/架构问题,而不是该语言特有的问题

我将为我的示例编写伪代码,所以请不要对语法进行区分

当我看到类继承的演示时,它通常是一些简单的东西,比如
类Student extensed Person(){}
。显然,学生是一个更专业的人,所以这是有道理的

在我的应用程序中,我有一个类
Site()
,其中包含相关信息,如DSN、文件上载目录等。我在一个类中执行SQL导出,在另一个类中执行文件上载导出,这两个类都是从Site类中调用的(伪代码):

Site类除了控制从应用程序前端请求的流量外,什么都不做,其他的都交给一个导出类

这对我来说很好,但我想正确地构建它。现在,我必须将站点的属性传递给导出类的构造函数,然后使用这些参数设置导出类的属性。这会导致大量重复

让我的导出类继承Site类是否合适,这样我就可以直接访问属性了?导出类并不像我前面给出的个人/学生示例那样是一个更专业的站点。相反,它们只是为Site类执行繁重的提升


如果这种情况不适合继承,我应该如何构造应用程序?正如我前面所说的,我现在所做的方法是有效的,但我想借此机会学习更多关于设计模式的知识,并以一种有意义的方式编写代码。

是另一回事时,应该使用继承

就像狗是一种动物,所以它应该从动物身上继承

导出不是一种站点,因此它不应该继承自它


你要找的是导出应包含对其导出的站点的引用。然后,您可以在构建时将Site对象传递给它,它可以随时从站点获取数据。

导出不应扩展站点类,因为导出的对象不是站点对象子集的成员。继承被过度使用了,在这种情况下,组合更有意义,正如您所做的那样。改进现有对象模型并提高可测试性的一种方法是使用依赖项注入

在对象实例中调用“new”会使对象模型更脆弱,更难测试。您可以将导出程序的实例注入Site类

旁注:我将导出类命名为名词(SQLExporter,而不是SQLExport),这一点更为明显。这是一种风格上的东西,但我认为它更好地传达了类的角色:它是一个被委派了导出数据角色的对象

public setSQLExporter( SQLExporter exporter )
{
   variables.sqlExporter = arguments.exporter;

   // set some properties on exporter here, like the DSN
   variables.sqlExporter.dsn = this.dsn;
}

public setUploadsExporter( UploadsExporter exporter )
{
   variables.uploadsExporter = arguments.exporter;

   // set some properties on exporter here, like the upload directory
   variables.uploadsExporter.uploadDirectory = this.uploadDirectory;
}

public function exportSql() {
    variables.sqlExporter.createDumpAndZipItAndStuff();

    // or maybe your generalize the interface to these exporters and do something like
    // variables.sqlExporter.export();
}

public function exportUploads() {
    variables.uploadsExporter.copyAndZipFilesAndStuff();

    // or maybe your generalize the interface to these exporters and do something like
    // variables.uploadsExporter.export();
}
然后,在您的主应用程序中(或任何首先实例化站点对象的应用程序):


这为使用模拟对象直接测试站点打开了大门,而无需与数据库或文件系统通信。如果您使用像ColdSpring这样的DI框架,该框架可以为您进行这种连接。

因此,当我从站点内创建新的导出类时,我应该执行类似于
Export=new Export(this)的操作?然后,在导出构造函数中,我应该定义一个属性来保存整个类,即
函数构造(对象站点){this.Site=Site;}
?是的,这就是想法。这样Export就知道他在为谁工作,并且你持有一个引用,这样数据就不会被复制。我想你指的是合成[1]而不是封装。封装就是隐藏实现。构图是将一个物体从碎片中组装起来的术语。[1] 我看到您正在将SQLExporter和UploadsPorter的引用传递给它们的相对
set()
函数,但是如果不是从Site对象内部实例化的话,对象实际上在哪里呢?在我的示例中添加了更多的代码来说明这是如何工作的。乍一看,它可能看起来像是更多的代码,但是独立于所有其他模块对每个代码模块进行单元测试的能力得到了很大的回报。感谢您花时间向我解释这一点。鉴于Yochai是如何回答眼前的具体问题的,我将留下他作为答案;然而,您的回答给了我很多思考的食物,我可能会在某个时候的重构期间实现DI。
public setSQLExporter( SQLExporter exporter )
{
   variables.sqlExporter = arguments.exporter;

   // set some properties on exporter here, like the DSN
   variables.sqlExporter.dsn = this.dsn;
}

public setUploadsExporter( UploadsExporter exporter )
{
   variables.uploadsExporter = arguments.exporter;

   // set some properties on exporter here, like the upload directory
   variables.uploadsExporter.uploadDirectory = this.uploadDirectory;
}

public function exportSql() {
    variables.sqlExporter.createDumpAndZipItAndStuff();

    // or maybe your generalize the interface to these exporters and do something like
    // variables.sqlExporter.export();
}

public function exportUploads() {
    variables.uploadsExporter.copyAndZipFilesAndStuff();

    // or maybe your generalize the interface to these exporters and do something like
    // variables.uploadsExporter.export();
}
thisSite = new Site(...);

// Get some Exporters
thisSite.setSQLExporter( new SQLExporter() );
thisSite.setUploadsExporter( new UploadsExporter() );

// Trigger exports
thisSite.exportSql();
thisSite.exportUploads();