Частное наследование и не виртуальные интерфейсы

Так что некоторое время я интересовался D, и некоторое время назад возился с ним. Я начал смотреть на это снова, и мне действительно нравится то, чего он пытается достичь, но у меня есть сомнения по поводу одного из моих любимых вариантов дизайна C++... не виртуальных интерфейсов.

Что мне нравится в этом проекте, так это то, что он позволяет выполнять предварительную и последующую проверку условий, ведение журналов и управление ресурсами на "вершине" иерархии наследования. Это позволяет разработчику определить все общие функциональные возможности группы связанных классов и разбить настраиваемые части класса на очень маленькие функции. Это также уменьшает количество функций, которые необходимо записать в подкласс. Кроме того, поскольку виртуальные точки расширения являются частными, они не загрязняют интерфейс и не позволяют пользователям напрямую вызывать специфичные для реализации функции (что действительно является ключевым).

Есть ли способ добиться этого в D?

Пример на C++ (непроверенный, не скомпилированный... только для иллюстрации).

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;

}

1 ответ

Решение

Конечно. Для не виртуальной функции-члена, либо сделайте это final (так что компилятор может оптимизировать его виртуальность) или шаблонизировать его, и тогда он гарантированно будет не виртуальным, поскольку функции шаблона никогда не являются виртуальными. Чтобы шаблонизировать функцию без параметров шаблона, просто дайте ей пустой список параметров шаблона. например

void setChip(RxChip chip) {...}

становится

void setChip()(RxChip chip) {...}

А для виртуальной функции просто сделайте это protected, В настоящий момент, private а также package никогда не являются виртуальными, поэтому, если вы хотите, чтобы функция была виртуальной, вы должны сделать это либо public или же protectedи делая это protected, это не доступно общедоступным API. Вы не можете пойти полноправным и сделать это private как и в C++, но, возможно, это все равно ничего вам не даст, так как переопределяющая функция все еще может вызываться классом, в котором она находится. Итак, все делает это private делает так, чтобы вы не могли вызывать версию базового класса (которая обычно является чисто виртуальной / абстрактной).

Тем не менее, я хотел бы отметить, что если все, что вам нужно, это контракты, D's in а также out блоки поддерживают полиморфизм. Таким образом, вам может даже не понадобиться NVI. В этот момент у вас будет просто функция базового класса in а также out блоки с тем, что в контрактах и ​​вне контрактов вы хотите, и они будут вызываться при вызове производной функции. Это просто работает для утверждений, которые вы хотите для предварительных и постусловий, но в некоторых случаях устраняет необходимость в NVI.

Другие вопросы по тегам