第8回 解答例

カプセル化する

演習の解答例。まず自分で書いてから読むこと。

Motorクラス

include/Motor.hpp
#pragma once

class Motor {
public:
    Motor(int pin);

    void begin();

    void setSpeed(int speed);
    int getSpeed() const;

    void stop();

private:
    void apply();       // 内部だけで使うので private

    int pin;
    int speed;
};
src/Motor.cpp
#include <Arduino.h>

#include "Motor.hpp"

namespace {
const int MIN_SPEED = 0;
const int MAX_SPEED = 100;
const int PWM_MAX = 255;
}

Motor::Motor(int pin)
    : pin(pin), speed(0) {
}

void Motor::begin() {
    pinMode(pin, OUTPUT);
    stop();
}

void Motor::setSpeed(int speed) {
    if (speed < MIN_SPEED) {
        speed = MIN_SPEED;
    }

    if (speed > MAX_SPEED) {
        speed = MAX_SPEED;
    }

    this->speed = speed;
    apply();
}

int Motor::getSpeed() const {
    return speed;
}

void Motor::stop() {
    setSpeed(MIN_SPEED);
}

void Motor::apply() {
    analogWrite(pin, speed * PWM_MAX / MAX_SPEED);
}

動作確認

Motor motor(24);

void setup() {
    Serial.begin(115200);
    motor.begin();

    motor.setSpeed(-100000);
    Serial.println(motor.getSpeed());   // 0

    motor.setSpeed(500);
    Serial.println(motor.getSpeed());   // 100

    motor.setSpeed(60);
    Serial.println(motor.getSpeed());   // 60
}

外部から何を渡しても、speedは必ず0〜100に収まる。これがカプセル化の効果である。

「外部に本当に必要な操作」を考える

Motorの場合、外から必要なのは次の3つだけで足りる。

操作公開理由
setSpeed()public速度を指示したい
stop()public止めたい。よく使うので専用に用意する
getSpeed()public現在値を知りたい
apply()privatePWMへの変換は内部都合。外は知る必要がない
pin / speedprivate直接書き換えられると制限を回避できてしまう

やりがちな誤り

全メンバにgetter/setterを機械的に付けるのはカプセル化ではない。

// 意味のないカプセル化
void setSpeed(int speed) { this->speed = speed; }   // 制限がない
int getPin() const { return pin; }                  // 外は pin を知る必要がない

setSpeed()に制限がなければ、privateにした意味がない。getPin()は内部の都合を漏らしているだけで、誰も使わない。

→ 第8回の本文に戻る