ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則

6.2K Views

November 30, 23

スライド概要

2023/11/30(木)に実施する社内勉強会、X スペース 【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則の資料です。

勉強会概要:
https://k-abe.connpass.com/event/297777/

Xスペース(録音)
https://twitter.com/i/spaces/1nAKEaOpWzVKL

profile-image

組込みソフトウェアエンジニア。 技術バックボーンはC言語・ベアメタル。 CQ EVカートのオーナーで、ハード・ソフトウェアの改造を通じて自身のスキルアップを日々考え中・・・。 LAPRASポートフォリオ: https://lapras.com/public/k-abe GitHub: http://github.com/grace2riku Qiita: https://qiita.com/juraruming Zenn: https://zenn.dev/k_abe よろしくね。

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

関連スライド

各ページのテキスト
1.

【連続講座】ソフトウェア設計原則 【SOLID】を学ぶ #5 リスコフの置換原則(Liskov Substitution Principle) パーソルクロステクノロジー株式会社 第1技術開発本部 第4設計部 設計2課 阿部耕二

2.

目次 自己紹介 SOLID について リスコフの置換原則(Liskov Substitution Principle)について 原則違反の例 サンプルコードについて 原則に則った例 今回の設計所感 設計についてのディスカッション・質問 参考資料 2

3.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 自己紹介 名前: 阿部 耕二(あべ こうじ) 所属: パーソルクロステクノロジー株式会社 第1技術開発本部 第4設計部 設計2課 医療機器の組込みソフトウェア開発。C言語。 趣味: 宇宙開発(リーマンサットプロジェクト広報メンバー) LAPRAS ポートフォリオ : https://lapras.com/public/k-abe Twitter: @juraruming 3

4.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 SOLID について 設計の5原則の頭文字をとったもの。 S 単一責務の原則( Single Respomsibility Principle ) O オープン・クローズドの原則( Open Closed Principle L リスコフの置換原則( Liskov Substitution Principle ) I インターフェイス分離の原則( Interface Segregation Principle ) D 依存関係逆転の原則( Dependency Inversion Principle ) 4

5.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 SOLID 原則の重要性 参考資料3より引用 凝集度が高くなる 他のモジュールと疎結合になる 各モジュールの目的が明確に分けられると、コード変更の際の影響 は局所化される。結果、テストしやすい設計になる。 上記の特徴を持つと再利用しやすいコードになる。 “ “ 5

6.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 リスコフの置換原則(Liskov Substitution Principle)について 出典: wikipedia サブタイプのオブジェクトはスーパータイプのオブジェクトの仕様 に従わなければならない 基底型オブジェクトを派生型オブジェクトで型安全に代替できるこ と ※この資料ではつぎの用語の定義とする スーパータイプ → 基底クラス・スーパークラス, サブタイプ → 派生 クラス・サブクラス 6

7.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 サンプルコードについて サンプルコードはこちらのGitHubリポジトリに格納している。 原則違反のサンプルコード サンプルコードコード内容 GitHubリポジトリのディレクトリ名 1. サブクラスに実装 no_lsp_add_impl_sub_class 2. 事前条件 ng_preconditions 3. 事後条件 ng_postconditions 4. 不変条件 ng_invaritants 5. 例外 ng_exception 7

8.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 原則に則ったサンプルコード サンプルコードコード内容 GitHubリポジトリのディレクトリ名 1. サブクラスに実装 ok_lsp_add_impl_sub_class 2. 事前条件 ok_preconditions 3. 事後条件 ok_postconditions 4. 不変条件 ok_invaritants 5. 例外 ok_exception 今回の設計所感 -> 継承で使われる意図がないことを明示する no_inheritance 8

9.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 サンプルコードの実行方法について make と C++ コンパイラが必要 GitHub リポジトリを自分のローカル環境に zip ダウンロードもしく はgit cloneする。 確認したいサンプルコードのディレクトリでmakeを実行する ディレクトリ名.appの実行ファイルができるので実行する 私の確認環境(MacOS, clangで確認) $ gcc -v Apple clang version 15.0.0 (clang-1500.0.40.1) Target: x86_64-apple-darwin22.6.0 Thread model: posix InstalledDir: /Library/Developer/CommandLineTools/usr/bin 9

10.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 原則違反の例 置換できない構造の例 1. サブクラスに実装している 2. 事前条件をサブクラスで強めている 3. 事後条件をサブクラスで弱めている 4. 不変条件をサブクラスで保持していない 5. サブクラスで独自の例外を投げている 10

11.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ 1. サブクラスに実装している 長方形と正方形のサンプル コードを例にして説明す る。 11

12.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 // Rectangle.hpp class Rectangle { protected: int width, height; public: Rectangle(const int width, const int height) : width(width), height(height) {} virtual void setWidth(const int w); virtual void setHeight(const int h); int getWidth() const ; int getHeight() const ; int area() const ; }; 12

13.
[beta]
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則
// Square.hpp
#include "Rectangle.hpp"
class Square : public Rectangle {
public:
Square(int size) : Rectangle(size, size) {}
void setWidth(const int w) override;
void setHeight(const int h) override;
};

13

14.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 // Rectangle.cpp #include "Rectangle.hpp" void Rectangle::setWidth(const int w) { width = w; } void Rectangle::setHeight(const int h) { height = h; } int Rectangle::getWidth() const { return width; } int Rectangle::getHeight() const { return height; } int Rectangle::area() const { return width * height; } 14

15.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 // Square.cpp #include "Rectangle.hpp" #include "Square.hpp" void Square::setWidth(const int w) { width = height = w; } void Square::setHeight(const int h) { width = height = h; } 15

16.
[beta]
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則
// no_lsp_add_impl_sub_class.cpp
#include <iostream>
using namespace std;
#include "Rectangle.hpp"
#include "Square.hpp"
void process(Rectangle& r) {
int w = r.getWidth();
r.setHeight(10);
std::cout << "expected area = " << (w * 10) << ", got " << r.area() << std::endl;
}

int main() {
Rectangle r(5, 5);
process(r); // expected area = 50, got 50
Square s(5);
process(s); // expected area = 50, got 100, LSP violation!
return 0;
}

16

17.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 関係(〜は〜である)が破綻しているため置換できない 正方形は長方形である。 長方形は 4 つの角度が同じ 二組の対辺が同じ長さ という特徴がある。 正方形もこの特徴がある。正方形は前述の特徴に加えてすべての辺が 等しいという特徴がある。 正方形も長方形の一種という判断で、この例題では正方形は長方形を 継承した。 is-a 17

18.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 事前条件をサブクラスで強めている 事前条件:メソッドの引数など 基底クラスの定義 2. // Parent.hpp #ifndef PARENT_HPP_ #define PARENT_HPP_ class Parent { public: virtual void doWork(int value); }; #endif // PARENT_HPP_ 18

19.
[beta]
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則

基底クラスの実装
value < 0 か判定している。
// Parent.cpp
#include <iostream>
#include "Parent.hpp"
using namespace std;

void Parent::doWork(int value) {
if (value < 0) {
throw std::invalid_argument("Parent requires value >= 0");
}
//
cout << "Parent value = " << value << endl;
}

作業をする

19

20.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 サブクラスの定義 // Child.hpp #include "Parent.hpp" class Child : public Parent { public: void doWork(int value) override; }; 20

21.
[beta]
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則

サブクラスの実装
value < 10 か判定している。基底クラスは value < 0 の判定だった。
事前条件を強化(条件が厳しく) しているため基底クラスとサブクラ
スが置換不可になっている。
// Child.cpp
#include <iostream>
#include "Child.hpp"
using namespace std;
void Child::doWork(int value) {
if (value < 10) {
throw std::invalid_argument("Child requires value >= 10"); //
}
//
cout << "Child value = " << value << endl;
}

事前条件を強化している

子クラス固有の作業をする

21

22.
[beta]
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則

実行結果: 基底クラスと同じ引数をサブクラスに指定した場合
// ng_preconditions.cpp
#include <iostream>
using namespace std;
#include "Parent.hpp"
#include "Child.hpp"
int main() {
Parent parent;
parent.doWork(0);

// Parent value = 0

Child child;
child.doWork(10);

// Child value = 10

例外発生

//
std::invalid_argument: Child requires value >= 10
child.doWork(0);
return 0;
}

22

23.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 事後条件をサブクラスで弱めている 事後条件:メソッドの戻り値など 基底クラスの定義 3. // Parent.hpp class Parent { public: virtual int getValue(); }; 23

24.
[beta]
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則

基底クラスの実装

// Parent.cpp
#include <iostream>
#include "Parent.hpp"
using namespace std;
int Parent::getValue() {
//
return 42;
}

常に正の値を返す

24

25.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 サブクラスの定義 // Child.hpp #include "Parent.hpp" class Child : public Parent { public: int getValue() override; }; 25

26.
[beta]
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則

サブクラスの実装

// Child.cpp
#include <iostream>
#include "Child.hpp"
using namespace std;
int Child::getValue() {
int val = Parent::getValue();

事後条件を弱化している(負の値を返す可能性がある)

//
return val - 50;
}

26

27.
[beta]
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則

実行結果: サブクラスが負の数を返している(基底クラスは正の数の戻
り値を想定している)
int main() {
int ret_val;
Parent parent;
ret_val = parent.getValue();
cout << "Parent return value = " << ret_val << endl;

// Parent return value = 42

Child child;
ret_val = child.getValue();
cout << "Child return value = " << ret_val << endl; // Child return value = -8
return 0;
}

27

28.
[beta]
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則

不変条件をサブクラスで保持していない
基底クラスの条件をサブクラスで保持していない、条件を緩めるなど
した場合
基底クラスの定義
4.

// Parent.hpp
class Parent {
protected:
int value; //
:
public:
Parent(int val) : value(val >= 0 ? val : throw std::invalid_argument("value must be non-negative")) {}
virtual void setValue(int val);
};

不変条件 常に正の数

28

29.
[beta]
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則

基底クラスの実装

// Parent.cpp
#include <iostream>
#include "Parent.hpp"
using namespace std;
void Parent::setValue(int val) {
if (val < 0) {
throw std::invalid_argument("value must be non-negative");
}
value = val;
cout << "Parent value = " << value << endl;
}

29

30.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 サブクラスの定義 // Child.hpp #include "Parent.hpp" class Child : public Parent { public: Child(int val) : Parent(val) {} void setValue(int val) override; }; 30

31.
[beta]
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則

サブクラスの実装

#include <iostream>
#include "Child.hpp"
using namespace std;
void Child::setValue(int val) {
if (val < -10) {
//
throw std::invalid_argument("Child requires value >= -10");
}
//
value = val;

親クラスよりも許容範囲を狭めている

基底クラスの不変条件「正の数」を破っている

cout << "Child value = " << value << endl;
}

31

32.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 実行結果: 基底クラスのメンバ変数は常に正の数というきまりをサブク ラスで破っている // ng_invaritants.cpp int main() { Parent parent(0); parent.setValue(1); Child child(0); child.setValue(-10); // Parent value = 1 // Child value = -10 return 0; } 32

33.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 サブクラスで独自の例外を投げている 基底クラスの定義 5. // Parent.hpp class Parent { public: virtual void doSomething(); }; 33

34.
[beta]
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則

基底クラスの実装

// Parent.cpp
#include <iostream>
#include "Parent.hpp"
using namespace std;
void Parent::doSomething() {
cout << "Parent -> doSomething() execute." << endl;
}

34

35.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 サブクラスの定義 // Child.hpp #include "Parent.hpp" class Child : public Parent { public: void doSomething() override; }; 35

36.
[beta]
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則

サブクラスの実装

// Child.cpp
#include <iostream>
#include "Child.hpp"
using namespace std;
void Child::doSomething() {
throw std::runtime_error("Error occurred"); //

基底クラスが予期しない例外を投げる

cout << "Child -> doSomething() execute." << endl;
}

36

37.
[beta]
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則

実行結果: 基底クラスは例外を投げないがサブクラスが例外を投げてい
る
// ng_exception.cpp
int main() {
Parent parent;
parent.doSomething();
Child child;
child.doSomething();

// Parent -> doSomething() execute.

//

例外発生:

std::runtime_error: Error occurred

return 0;
}

37

38.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 原則に則った例 つぎの原則違反を改善する。 1. サブクラスに実装している 2. 事前条件をサブクラスで強めている 3. 事後条件をサブクラスで弱めている 4. 不変条件をサブクラスで保持していない 5. サブクラスで独自の例外を投げている 38

39.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 【原則違反改善例】サブクラスに実装している 長方形の基底クラスを継承した正方形サブクラスの件 正方形の幅もしくは高さを設定すると幅&高さを変更するので長方形 の特徴を維持できなかった。 これを改善するには 正方形は長方形の継承をやめる 長方形、正方形の共通のインタフェースを定義する 共通のインターフェースを長方形、正方形で実装するようにする。 1. 39

40.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 左: 変更前のクラス図 右: 変更後のクラス図 40

41.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 共通インターフェース: 長方形、正方形はこのクラスを実装する // Shape.hpp class Shape { public: virtual int area() const = 0; virtual int getWidth() const = 0; virtual int getHeight() const = 0; virtual void setWidth(const int w) = 0; virtual void setHeight(const int h) = 0; virtual ~Shape() {} }; 41

42.
[beta]
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則

長方形の定義

// Rectangle.hpp
#include "Shape.hpp"
class Rectangle : public Shape {
protected:
int width, height;
public:
Rectangle(const int width, const int height) : width(width), height(height) {}
int area() const override;
int getWidth() const override;
int getHeight() const override;
void setWidth(const int w) override;
void setHeight(const int h) override;
};

42

43.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 長方形の実装 // Rectangle.cpp #include "Rectangle.hpp" int Rectangle::area() const { return width * height; } int Rectangle::getWidth() const { return width; } int Rectangle::getHeight() const { return height; } void Rectangle::setWidth(const int w) { width = w; } void Rectangle::setHeight(const int h) { height = h; } 43

44.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 正方形の定義 // Square.hpp #include "Shape.hpp" class Square : public Shape { private: int size; public: Square(int size) : size(size) {} int area() const override; int getWidth() const override; int getHeight() const override; void setWidth(const int w) override; void setHeight(const int h) override; }; 44

45.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 正方形の実装 // Square.cpp #include "Square.hpp" int Square::area() const { return size * size; } int Square::getWidth() const { return size; } int Square::getHeight() const { return size; } void Square::setWidth(const int w) { size = w; } void Square::setHeight(const int h) { size = h; } 45

46.
[beta]
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則

実行結果: 正方形・長方形は共通インターフェースを実装し、長方形・
正方形それぞれ独自の実装を進めることができた。
// ok_lsp_add_impl_sub_class.cpp
void process(Shape& shape) {
int w = shape.area() / shape.getHeight();
shape.setHeight(10);
std::cout << "expected area = " << (w * 10) << ", got " << shape.area() << std::endl;
}
int main() {
Rectangle r(5, 5);
process(r); // expected area = 50, got 50
Square s(5);
process(s); // expected area = 100, got 100, LSP is not violation!
return 0;
}

46

47.
[beta]
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則
2.

【原則違反改善例】事前条件をサブクラスで強めている
サブクラスは基底クラスと同じ事前条件にするか、もしくは事前条
件を弱く(緩めれば)すれば置換可能。

// Child.cpp
void Child::doWork(int value) {
//
if (value < 10) {
//
throw std::invalid_argument("Child requires value >= 10"); //
//
}

事前条件を強化している

基底クラスの事前条件を維持 (または下記を削除することで事前条件を緩める)

//
Parent::doWork(value);

子クラス固有の作業をする

//
cout << "Child value = " << value << endl;
}

47

48.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 3. 【原則違反改善例】事後条件をサブクラスで弱めている サブクラスは基底クラスと同じ事後条件にするか、もしくは事前条 件を強く(より大きな値に)すれば置換可能。 // Child.cpp int Child::getValue() { int val = Parent::getValue(); // 事後条件を弱化している(負の値を返す可能性がある) // LSP NG: return val - 50; 事後条件を強化している(より大きな正の値を返す) // LSP OK: return val + 10; } 48

49.
[beta]
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則
4.

【原則違反改善例】不変条件をサブクラスで保持していない
基底クラスの不変条件を使えば置換可能

// Child.cpp
void Child::setValue(int val) {
// LSP NG:
//
if (val < -10) {
//
throw std::invalid_argument("Child requires value >= -10");
//
}
//
//
//
value = val;

親クラスよりも許容範囲を狭めている

基底クラスの不変条件「正の数」を破っている
親クラスの不変条件を保持する

// LSP OK:
Parent::setValue(val);

cout << "Child value = " << this->value << endl;
}

49

50.
[beta]
【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則

【原則違反改善例】サブクラスで独自の例外を投げている
基底クラスが予期しない例外をなげなければ置換可能
5.

// Child.cpp
void Child::setValue(int val) {
void Child::doSomething() {
// LSP NG:
//
throw std::runtime_error("Error occurred");

基底クラスが予期しない例外を投げる

基底クラスが予期しない例外は投げない

// LSP OK:
cout << "Child -> doSomething() execute." << endl;
}

50

51.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 今回の設計所感 僕たちは何故リスコフの置換原則を破るのか? 2. リスコフの置換原則を破るとどんな未来が待っているか ? 3. 継承以外の解決方法 4. 継承で使われる意図がないことを明示する 5. 継承は使ってはダメなのか ? 1. 51

52.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 1. 僕たちは何故リスコフの置換原則を破るのか? なぜリスコフの置換原則を破ってしまうのか、考えられる原因を推 測した。 似ているから基底クラスを継承し、サブクラスを独自に拡張する 基底クラスの機能を使いたいから継承し、サブクラスを独自に拡張 する 52

53.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 【似ているから基底クラスを継承する、基底クラスの機能を使いたい から】の背景にありそうなもの 基底クラスを再利用したい、共通化したいという思惑 基底クラスとこれからつくるクラスは似ていると勘違いする DRY 原則に反していると思ってしまう 開発対象が別物(違うシステム、違う装置)であれば素直にクラス を別にするという選択をした方が幸せになれそう。 違うシステム、違う装置はそれぞれ別の事情で変化する。 53

54.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 2. リスコフの置換原則を破るとどんな未来が待っているか? そもそも構造がおかしくなる(長方形を継承した正方形の例) クライアント(サブクラスを使う側)に条件分岐が発生する。 この場合は基底クラスを使う、こっちの場合はサブクラスを使う、 という感じ。 サブクラスが増えればクライアントの複雑度は増していく。 結果、修正に閉じることが出来なくてOpen closed原則にも違反す る。 54

55.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 継承以外の解決方法 継承を使わない解決方法として 抽象に実装する がひとつの解決方法である。 継承したサブクラスのみに実装を追加していくとクライアントコード に基底クラスとサブクラスの場合分けの条件分岐が発生する、だっ た。 クライアントコードからは使うクラスを同一視したい。これはOpen closed 原則にもでてきた抽象に実装するで解決できる。 3. 55

56.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ 長方形を継承した正方形の原 則違反コードの改善例を再掲 する 左: 変更前のクラス図 右: 変更後のクラス図 56

57.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 継承で使われる意図がないことを明示する 継承を使わないでクラスを追加したい、ということを仕組みで解決す ることはできないか? 例えばこんなケース 派生開発の案件 設計の経験が浅い後輩が追加機能を実装することになった 既存のコードに似ている機能の追加 先輩設計者であるあなたは継承を使って実装はしない方針にしたい という思惑がある 4. 57

58.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 であればfinal, C#であればsealedのキーワードで継承を禁止する ことができる。 クラス定義時にデフォルトfinalにしておくようにすれば(社内のコー ディング規約で決めるなど)継承によるリスコフ置換原則違反を防止 することができる。 設計者の意図(このクラスは継承することを前提にしていない)を明 示することもできる。 C++ // Rectangle.hpp class Rectangle final { } Rectangle を継承するコードはコンパイルエラーになる。 58

59.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 継承は使ってはダメなのか? 継承を使わないことを推奨するようなことばかり書いてきた。 新しい言語も継承の機能がなかったりする。 継承は使わないほうが良いのか? 継承が効力を発揮する場面はないのか? 継承の使い所についてつぎの動画でひとつの解を提示してくれてい る。 オブジェクト指向の原則2:リスコフの置換原則と継承以外の解決方 法 #48_ 共通要素をまとめる継承 #49_ 共通要素をまとめる継承の実装例 5. 59

60.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 動画ではWindows FormアプリケーションのTextのコントロール部品 のクラスを継承して拡張する例が紹介されていた。 Text に入力されていた文字数が xx 文字以上だったら入力文字の色を xx にする、みたいな動き アプリケーションの全体、複数画面で統一感のある挙動を設定できる などの効果がある。 継承も使いところによっては有効な場面があると思うので探していき たいと感じた。 60

61.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 継承の使いところとして個人的に良いかもしれないと思ったこと Windows Form の Text の例のように画面の部品のクラスを継承して 拡張する サードパーティー製のライブラリを継承して自分たちの開発対象向 けに拡張する、みたいな場面 61

62.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 設計についてのディスカッション・質 問 自分以外の設計の視点が学びになると個人的に考えています。 ぜひぜひお気軽にフィードバックをよろしくお願いします こちらに学習の振り返りに使う目的でZennのスクラップを用意しま した。 活用ください。 【SOLID原則】#5 "リスコフの置換原則(Liskov Substitution Principle ) " の勉強会後の振り返り 62

63.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 参考資料 オブジェクト指向の原則2:リスコフの置換原則と継承以外の解決 方法 Udemy の講座。作成者はピーコック アンダーソンさん。リスコフの 置換原則以外のSOLID原則の講座もあり。 2. オブジェクト指向習得のための5ステップ【 SOLID 原則】 3. テスト駆動開発による組み込みプログラミング ―C 言語とオブジェク ト指向で学ぶアジャイルな設計 1. 63

64.

【連続講座】ソフトウェア設計原則【SOLID】を学ぶ #5 リスコフの置換原則 ご清聴ありがとうございました 64