我正在开发一个非常非常简单的数据访问层(DAL),其中包含两个类:DataTransferObject
(DTO)和DataAccessObject
(DAO)。这两个类都是抽象基类,需要为特定用例继承和修改。
class DataTransferObject {
protected:
//protected constructor to prevent initialization
};
class DataAccessObject {
public:
virtual bool save(DataTransferObject o) = 0;
virtual DataTransferObject* load(int id) = 0;
};
如果来自业务逻辑层的House
类,DAL类的实现将按以下方式读取:
class Dto_House : public DataTransferObject {
public:
int stories;
string address; //...which are all members of the House class...
Dto_House(House h);
};
class Dao_House : public DataAccessObject {
public:
bool save(Dto_House h) { /*...implement database access, etc...*/ }
Dto_House* load(int id) {/*...implement database access, etc...*/ }
};
编辑:当然,派生类知道House
类的结构和数据存储。
简单,漂亮,okidoke。
现在,我想在DTO类中提供方法toObject()
,以便快速将Dto_House
转换为House
对象。然后我读了关于C ++ 14中的自动返回类型推导并尝试过:
class DataTransferObject {
public:
virtual auto toObject() = 0;
};
但我不得不发现:没有虚拟功能的自动返回类型推论。 :(
对于针对此特定情况实施“具有推导返回类型的虚拟函数”,您有什么想法?我想在我的DTO“界面”中使用常规toObject()
函数。
我想到的唯一一件事就是:
template <typename T>
class DataTransferObject {
virtual T toObject() = 0;
};
class Dto_House : public DataTransferObject<House> {
public:
int stories;
string address;
House toObject() {return House(stories, address);}
};
修改 可能的用例是:
House h(3, "231 This Street");
h.doHouseStuff();
//save it
Dto_House dtoSave(h);
Dao_House dao;
dao.save(dtoSave); //even shorter: dao.save(Dto_House(h));
//now load some other house
Dto_House dtoLoad = dao.load(id 2);
h = dtoLoad.toObject();
h.doOtherHouseStuff();
但是房子不知道它可以保存和装载。
当然,可以导出抽象DAO类以进一步细化它以供例如用于Sqlite,XML文件或其他......我刚才提出了非常基本的概念。
答案 0 :(得分:1)
如何设置一个空的抽象类 - 实际上是一个接口,然后让两个类型实现它并将其设置为toObject
返回引用类型?
class Transferable
{
virtual ~Transferable() = 0;
}
然后:
class DataTransferObject {
public:
//Return a reference of the object.
virtual Transferable& toObject() = 0;
};
Dto_House : public DataTransferObject, Transferable { /*...*/ }
House : public DataTransferObject, Transferable { /*...*/ }
上面的例子是我的观点。
更好的是,您可以将DataTransferObject
用于此原因作为返回的引用类型,而不是其他抽象类:
class DataTransferObject {
public:
virtual DataTransferObject& toObject() = 0;
};
Dto_House : public DataTransferObject { /*...*/ }
House : public DataTransferObject { /*...*/ }
更新:如果您希望将类分开,按惯例分隔数据和操作之间的任何关联,您可以在表示数据的内容上设置基类的名称,即: Building,Construction etc,然后将其用于toObject
中的引用类型。
您还可以让类在数据操作API上操作这些操作。
答案 1 :(得分:0)
通常,您不能让virtual
函数在不同的子类中返回不同的类型,因为这违反了静态类型语言的整个概念:如果您调用DataTransferObject::toObject()
,编译器不知道它将在运行时返回什么类型。
这突出了你的设计的主要问题:为什么你需要一个基类?你打算如何使用它?调用DataTransferObject::toObject()
,即使你使用一些魔术来使其工作(或使用动态类型语言),听起来也不错,因为你无法确定返回类型是什么。无论如何,您将需要一些演员表或一些if
等来使其正常工作 - 或者您将仅使用所有此类对象(House
,Road
等常用的功能。) - 但是你只需要为它们所有人提供一个共同的基类。
实际上,相同的返回类型规则有一个例外:如果返回指向类的指针,则可以使用Covariant return type概念:子类可以覆盖virtual
函数以返回原始返回类型的子类。如果你所有的&#34;对象&#34;有一个共同的基类,你可以使用
struct DataTransferObject {
virtual BaseObject* toObject() = 0;
};
struct Dto_House : public DataTransferObject {
virtual House* toObject() { /*...*/ } // assumes that House subclasses BaseObject
};
然而,这仍然会留下同样的问题:如果您的代码中只有DataTransferObject
,即使您(但不是编译器)知道,它也是{{1}你需要一些演员,这可能是不可靠的。
另一方面,你的模板解决方案似乎相当不错,除了你无法显式调用Dto_House
(除非你知道对象的类型),但这是一个坏主意,因为我已解释过。
所以,我建议您考虑如何实际使用基类(甚至编写一些示例代码),并根据它做出选择。