본문 바로가기
프로그래밍 언어/C,C++

C++ runtime polymorphism과 constructor injection, property injection

by pagehit 2022. 2. 28.
반응형

enum class를 이용한 runtime polymorphism

 

#include <cstdio>
#include <stdexcept>

struct FileLogger {
  void log_transfer(long from, long to, double amount) {
    --snip--
    printf("[file] %ld,%ld,%f\n", from, to, amount);
  }
};

struct ConsoleLogger {
  void log_transfer(long from, long to, double amount) {
    printf("[cons] %ld -> %ld: %f\n", from, to, amount);
  }
};

enum class LoggerType {
  Console,
  File
};

struct Bank {
  Bank() : type { LoggerType::Console } { }
  void set_logger(LoggerType new_type) {
    type = new_type;
  }

  void make_transfer(long from, long to, double amount) {
    --snip--
    switch(type) {
    case LoggerType::Console: {
      consoleLogger.log_transfer(from, to, amount);
      break;
    } case LoggerType::File: {
      fileLogger.log_transfer(from, to, amount);
      break;
    } default: {
      throw std::logic_error("Unknown Logger type encountered.");
    } }
  }
private:
  LoggerType type;
  ConsoleLogger consoleLogger;
  FileLogger fileLogger;
};

int main() {
  Bank bank;
  bank.make_transfer(1000, 2000, 49.95);
  bank.make_transfer(2000, 4000, 20.00);
  bank.set_logger(LoggerType::File);
  bank.make_transfer(3000, 2000, 75.00);
}
--------------------------------------------------------------------------
[cons] 1000 -> 2000: 49.950000
[cons] 2000 -> 4000: 20.000000
[file] 3000,2000,75.000000

 

implementation-inheritance를 이용한 runtime polymorphism. 이는 anti-pattern.

 

#include <cstdio>

struct Logger {
  virtual ~Logger() = default;
  virtual void log_transfer(long from, long to, double amount) = 0;
};

struct ConsoleLogger : Logger {
  void log_transfer(long from, long to, double amount) override {
    printf("%ld -> %ld: %f\n", from, to, amount);
  }
};

 

아래는 constructor injection

 

#include <cstdio>

struct Logger {
    virtual ~Logger() = default;
    virtual void log_transfer(long from, long to, double amount) = 0;
};

struct ConsoleLogger : Logger {
    void log_transfer(long from, long to, double amount) override {
        printf("[cons] %ld -> %ld: %f\n", from, to, amount);
    }
};

struct FileLogger : Logger {
    void log_transfer(long from, long to, double amount) override {
        printf("[file] %ld, %ld, %f\n", from ,to, amount);
    }
};

struct Bank {
    Bank(Logger& logger) : logger{ logger } { }
    void make_transfer(long from, long to, double amount) {
        logger.log_transfer(from, to, amount);
    }
private:
    Logger& logger;
};

int main() {
    ConsoleLogger logger;
    Bank bank{ logger };
    bank.make_transfer(1000, 2000, 49.95);
    bank.make_transfer(2000, 4000, 20.00);
}

 

아래는 property injection.

pointer는 reference와 달리 reseat될 수 있으므로, 여기서는 pointer를 사용

 

#include <cstdio>

struct Logger {
    virtual ~Logger() = default;
    virtual void log_transfer(long from, long to, double amount) = 0;
};

struct ConsoleLogger : Logger {
    void log_transfer(long from, long to, double amount) override {
        printf("[cons] %ld -> %ld: %f\n", from, to, amount);
    }
};

struct FileLogger : Logger {
    void log_transfer(long from, long to, double amount) override {
        printf("[file] %ld, %ld, %f\n", from ,to, amount);
    }
};

struct Bank {
    void set_logger(Logger* new_logger) {
        logger = new_logger;
    }
    void make_transfer(long from, long to, double amount) {
        if (logger) logger->log_transfer(from, to, amount);
    }
private:
    Logger* logger{};
};

int main() {
    ConsoleLogger console_logger;
    FileLogger file_logger;
    Bank bank;
    bank.set_logger(&console_logger);
    bank.make_transfer(1000, 2000, 49.95);
    bank.set_logger(&file_logger);
    bank.make_transfer(2000, 4000, 20.00);
}

 

property injection을 사용하는 경우 nullptr인지 검사할 필요가 있다.

 

#include <cstdio>
struct Logger {
  --snip--
};

struct Bank {
  Bank(Logger* logger) : logger{ logger }{} ➊
  void set_logger(Logger* new_logger) { ➋
    logger = new_logger;
  }
  void make_transfer(long from, long to, double amount) {
    if (logger) logger->log_transfer(from, to, amount);
  }
private:
    Logger* logger;
};

 

반응형

댓글