According to Why define a Java object using interface (e.g. Map) rather than implementation (HashMap), I know I should declare the most abstract type possible, but the question is about template class or containers, how about the non container class? For example, suppose I'm using a UI framework, cocos2d-x, which Label and Sprite are extended from Node, and the code to create the screen of UI:
Sprite* titleSprite=Sprite::create("welcome.png");
this->addChild(titleSprite);
titleSprite->setPosition(300,100);
Label* titleLabel=Label::create("Welcome,"+userInfo.name,"customFontType",32);
this->addChild(titleLabel);
//titleLabel->setFontColor(Color.RED);
titleLabel->setPosition(360,100);
It is not declaring most abstract type, so modified as follows:
Node* title1Node=Sprite::create("welcome.png");
this->addChild(title1Node);
title1Node->setPosition(300,100);
Node* title2Node=Label::create("Welcome,"+userInfo.name,"customFontType",32);
this->addChild(title2Node);
//title2Node->setFontColor(Color.RED);
title2Node->setPosition(360,100);
However, I found some disadvantages after declaring most abstract type:
- It becomes harder to find a specific label from the variable name, eg: if I want to change a specific label, I may try to search for suffix of variables first, declaring most abstract type erases the extra information about the variables from suffix. Note: I don't want to declare with Node but name with suffix Sprite,eg:
Node* titleSprite=Sprite::create("welcome.png");
because I think it is misleading to have a variable name suffix different from the type actually.
- There may some test code, which depends on the child type, eg:
//titleLabel->setFontColor(Color.RED);
which is necessary for testing (eg: let a new teammate to see the actual Label in the screen easily), but not required in production. Declaring with most abstract type voids the comment code, and becomes less convenient to run the test code.
- If there are many UI components with same suffix, it may increase the chance of typing the desired UI variable name incorrectly, eg:
non most abstract type:
Sprite* brickSprite=...
Label* blockLabel=...
//brickLabel->setVisible(false);
//wrongly typing "brickLabel" would not compile because brickLabel does not exist
most abstract type:
Node* brickNode=...
Node* blockNode=...
//brickNode->setVisible(false);
//wrongly typing "brickNode" would compile...
- I may accidentally using the wrong UI because of less strict typing, similar to problem of "primitive obsession", eg: I want to show fade in effect on Sprite but not Label (Which the fade in function is on parent class Node):
non most abstract type version:
class MyUIPageNode : Node{
private:
Sprite* sprite=NULL;
Label* label=NULL;
virtual void init(){
.
.
.
//interchanges 2 UI accidentally
this->sprite=titleLabel;
this->label=titleSprite;
this->sprite->runAction(FadeIn::create(0.5));
//not compile because titleLabel is not Sprite
}
}
most abstract type version:
class MyUIPageNode : Node{
private:
Node* sprite=NULL;
Node* label=NULL;
virtual void init(){
.
.
.
//interchanges 2 UI accidentally
this->sprite=titleLabel;
this->label=titleSprite;
this->sprite->runAction(FadeIn::create(0.5));
//can compile, and fadeIn the Label actually instead of Sprite
}
}
I think the code (especially for UI framework) declaring the exact type is more readable , more natural and less error prone. So my question is, are the problems above reasons to not declaring most abstract type?