kg-icon
プログラミングパラダイムについてまとめる
2025-02-15

はじめに

プログラミングパラダイムの勉強をもう一度やろうと思ったのでまとめておきます。

構造化プログラミング

「GoTo文は有害である」とエドガー・ダイクストラが提唱し生まれたプログラミング手法。

時代を経るにつれてソフトウェア開発がより複雑にかつ機能も膨大になっていった結果、ごく一部のすごく優秀なプログラマーだけで開発し続けるのはかなり厳しくないか…?というのが構造化プログラミングが生まれた主な背景らしい。

つまり、天才ではない人でもソフトウェア開発を安心安全に、かつチームで開発を行えるような環境を整える必要が生まれました。

そして、より詳細なところを詰めていくと、構造化プログラミングは以下を使いプログラミングすればいい感じに書けるんじゃないか!?という考えです。

  • 順次
  • 選択
  • 反復

一貫した制御構造を使うことで他の開発者でも理解がしやすくなります。また、関数を使うことでコードをブロックのように管理でき保守性も向上します。

例えば以下はGoTo文。

basic
110 INPUT A
220 IF A > 0 THEN GOTO 50
330 PRINT "A is not positive"
440 GOTO 10
550 PRINT "A is positive"
660 END

このようなコードは、行番号を参照しながらプログラムの流れを追う必要があり、規模が大きくなるほど理解が困難になっていきます。

また、このようなコードだと理解するだけではなく変更するのも非常に難しくなります。バグも発生しやすいです。

構造化プログラミングはこのような複雑さを解消します。

順次処理

typescript
1// 順番通りに処理を行う
2const x: number = 10;
3const y: number = x + 5;
4console.info(y); // 15

選択処理

typescript
1// 条件に応じた処理の分岐
2const score: number = 85;
3
4if (score >= 90) {
5  console.info("Excellent!");
6} else if (score >= 70) {
7  console.info("Good");
8} else {
9  console.info("Needs Improvement");
10}

反復処理

typescript
1// 繰り返し処理
2for (let i = 0; i < 5; i++) {
3  console.info(`Iteration: ${i}`);
4}

おまけ: 関数活用

typescript
1// 関数を使ってコードを整理
2const add = ({ a, b }: { a: number, b: number }): number => a + b;
3
4const result = add({ a: 10, b: 5 });
5console.info(result); // 15

オブジェクト指向プログラミング(OOP)

最初にオブジェクト指向として生まれた言語はSimulaというプログラミング言語らしいです。

オブジェクト指向プログラミングは今でも実際の現場で頻繁に耳にします。3日1回くらいのペースで耳にするんじゃないかというくらい身近ではあります。

ただ実際のところ、ガッチリ決められた定義はなく言葉が一人歩きしている感は否めません。自分は以下を遵守したものをオブジェクト指向と考えています。

  • カプセル化
  • ポリモーフィズム

クラスを使ったプログラミング = オブジェクト指向だとは考えていません。

カプセル化

カプセル化は、相互に関連する振る舞いやデータをオブジェクトの中に閉じ込めるというもの。

クラスを使うとカプセル化しやすいと思います。

typescript
1class Person {
2  name: string;
3  age: number;
4
5  constructor(name: string, age: number) {
6    this.name = name;
7    this.age = age;
8  }
9
10  greet(): void {
11    console.info(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
12  }
13}

ポリモーフィズム

自分はポリモーフィズムを「差し替え可能な実装を可能にするための振る舞いを持つ概念」と考えています。

以下のコードだと、sendNotification関数はメールだろうがSMSだろうが関係なくsendする。ここに新たなLineNotificationやMobilePushNotificationが追加されても問題なし。

通知の送信方法が異なるクラスでも、sendNotification関数は共通のNotification型で処理でき、異なる実装を簡単に差し替えることができますね。

typescript
1// 通知のインターフェース
2interface Notification {
3  message: string;
4  send(): void;
5}
6
7// メール通知のクラス
8class EmailNotification implements Notification {
9  message: string;
10  email: string;
11
12  constructor(message: string, email: string) {
13    this.message = message;
14    this.email = email;
15  }
16
17  // メール送信方法を実装
18  send(): void {
19    console.info(`Sending Email to ${this.email}: ${this.message}`);
20  }
21}
22
23// SMS通知のクラス
24class SMSNotification implements Notification {
25  message: string;
26  phoneNumber: string;
27
28  constructor(message: string, phoneNumber: string) {
29    this.message = message;
30    this.phoneNumber = phoneNumber;
31  }
32
33  // SMS送信方法を実装
34  send(): void {
35    console.info(`Sending SMS to ${this.phoneNumber}: ${this.message}`);
36  }
37}
38
39// 通知を送信する関数
40function sendNotification(notification: Notification): void {
41  notification.send();
42}
43
44// ポリモーフィズムの利用
45const emailNotification = new EmailNotification("Hello, you've got a new message!", "[email protected]");
46const smsNotification = new SMSNotification("Hello, you've got a new SMS!", "123-456-7890");
47
48sendNotification(emailNotification);
49sendNotification(smsNotification);

関数型プログラミング

代数的データ型

和型は、複数の型のいずれかであることを表現する型です。

f#
1type Status =
2    | Before of { Id: int }
3    | Ongoing of { Id: int; StartDate: DateTime }
4    | Done of { Id: int; StartDate: DateTime; EndDate: DateTime }
5
6type Reservation =
7    | Before of Status
8    | Ongoing of Status
9    | Done of Status

積型は、複数の型を1つの型にまとめる方法です。

f#
1type Reservation = 
2    { Id: int
3      Status: string    // 状態(Before, Ongoing, Doneなど)
4      StartDate: DateTime option  // 開始日(状態に応じて有無が異なる)
5      EndDate: DateTime option }  // 終了日(状態に応じて有無が異なる)