So I've been interested in D for a while now and I messed about with it a while ago. I've started to look at it again and I really do like what it is trying to achieve, but I have a qualm about one favourite C++ design option of mine... non-virtual interfaces.
What I like about this design is that it allows for pre and post condition checking, logging and resource management to happen at the "top" of the hierarchy of inheritance. This allows the designer to specify all the common functionality of a group of related classes and to break the customisable parts of the class into very small functions. It also reduces the amount of functionality that needs to be written in a sub-class. Plus, because the virtual extension points are private, it doesn't pollute the interface, or allow users to call the implementation-specific functions directly (that is really key).
Is there a way to achieve this in D?
Example in C++ (untested, uncompiled... just for illustration).
class Radio{
public:
Radio( std::string id, Station defaultStation, RxChip chip)
:defaultStation(defaultStation),
id(id),
chip(chip){
}
void turnOn() {
log.trace("Radio turned on: id:[%s]", id.c_str());
doEnableRx();
doPostEnable();
setToStation(defaultStation);
}
void turnOff(){
log.trace("Radio turned off: id:[%s]", id.c_str());
doDisableRx();
doPowerOff();
}
void tune(){
log.trace("Tuning");
findAllStations();
}
void setToStation(Station target){
logStationChange(target);
doSetRxChipPassFilter(target);
}
void setChip(RxChip chip) {
rxChip = chip;
}
RxChip getChip() {
return rxChip;
}
private:
// doesn't start with "do" as this is considered a "normal" virtual function.
virtual void findAllStations(){
chip.setFrequency(chip.getLowFreq());
setChipToNextTunedPoint();
Station stat( chip.getFrequency(), tunedStations.size() );
tunedStations.push_back(stat);
}
virtual bool setChipToNextTunedPoint() {
if(chip.isTuned()) {
while( isTuned && chip.getFrequency() < chip.getHighFreq() )
chip.incrementFreq();
}
while( !chip.isTuned() && chip.getFrequency() < chip.getHighFreq() )
chip.incrementFreq();
return chip.isTuned();
}
// "do" functions are considered mandatory extension points for sub-classes
virtual void doEnableRx() = 0;
virtual void doPostEnable() = 0;
virtual void doDisableRx() = 0;
virtual void doPowerOff() = 0;
virtual void doSetRxChipPassFilter(Station target) = 0
{
//default implementation but it must be specified for use by sub-class.
chip.setFrequency(target.getLowFreq());
while( !chip.isTuned() && chip.getFrequency() < station.getHighFreq() ) {
chip.incrementFreq();
}
}
Station defaultStation;
std::vector<Station> tunedStations;
RxChip chip;
}