ソフトウェア アーキテクチャが知っておくべきさまざまなプログラミング パラダイム | ラブプリート シンクリーンなアーキテクチャ | 2022年11月

この記事は、Robert C. Martin 著の本 Clean Architecture に基づくシリーズの一部です。 トピックの見出しのみが本から取られ、何が有用で受け入れられるかを見つけるためにソフトウェア コミュニティで調査されます。 クリックするとシリーズの前の記事を読むことができます ここ. 私と一緒に学びたい場合は、今後の記事やビデオを購読してください。

私がソフトウェア エンジニアリングの分野に携わってから何年も経ちましたが、さまざまなプログラミング パラダイムが何を意味するのかを明確に理解する機会はありませんでした。 1 つについて読もうとしても、宣言型、命令型、構造化、手続き型、関数型、論理型、オブジェクト指向など、さまざまなプログラムの書き方など、多くの用語が飛び交っています。 これだけでなく、いくつかの用語の間には非常に細い境界線があり、それらを区別するのは困難です。

しかし、今回は、明確に理解するために、用語を 1 つずつ調べ、記事を読み、ビデオを見て、さらには 1970 年代 (これらの用語がブームだった時期) の研究論文を読む必要がありました。 主要なプログラミング パラダイムと、なぜそれが必要なのかを説明しようと思います。 明確な答えを出すのが難しい点もありますが、全体として、この記事を読めばかなりよく理解できます。 開発者が問題をコーディング、解決、および考える方法は、プログラミング パラダイムの影響を受ける可能性があります。

少し長くなりますが、今日は何か面白くて新しいことを学べることをお約束します!

プログラムを書くときは、通常、次の 2 つのことを念頭に置いています。 何をどのように行うか? プログラムで何をしたいのかを知る必要があるため、何をすべきかの最初の部分は常に必要です。 さて、それを行う方法についての指示も提供する場合、それを 命令型パラダイム. 一方、指示を出さない場合は、 宣言的パラダイム. how 部分は、言語の実装に内部的に残されています。

Java、C++、Javascript などのほとんどのプログラミング言語は、命令型パラダイムに従っています。 たとえば、Javascript でアルファベット順に 90 点以上の上位 10 人の生徒を見つけたい場合は、その方法を段階的に説明する必要があります。

1. const students = readData(); 
2. const filtered = students.filter(s => s.marks > 90);
3. const firstTen = filtered.slice(0,10);
4. firstTen.sort((a, b) => a.name.localeCompare(b.name))

最も一般的な宣言型言語には、データベース クエリ言語 (SQL)、正規表現、関数型プログラミングなどが含まれます。 このパラダイムでは、クエリで何を行うか、言語によってその部分がどのように処理されるかについてのロジックを提供するだけで済みます。 上記と同じ例を取ると、SQL クエリは次のようになります。

SELECT * FROM students WHERE marks > 90 ORDER BY name LIMIT 10

明示的な手順を使用せずに、クエリでロジックを提供しただけです。 より興味深く人気のある命令型パラダイムを見てみましょう。

すべての基礎となるパラダイムを見てみましょう。 手続き型パラダイム.

手続き型パラダイム

このパラダイムの背後にある基本的な概念は、割り当て、プロシージャ (関数) 呼び出しなどの命令を順次実行することです。 大きなプログラムは、再利用可能でメイン プログラムまたは他の関数から呼び出される小さな関数を使用して分割されます。 通常、メインプログラムにはデータがあり、手順はそのデータを処理して問題を解決します。 たとえば、以下は生徒の成績を公開するための手続き型プログラムです。

// Data
const students = readStudents()
const marks = readMarks()
// Procedure calls
addMarksToStudents(students,marks)
calculatePosition(students)
sortByPosition(students)
publishScoreSheet(students)

プログラムは常に行ごとに実行されるとは限らず、次の場合に行をスキップするなど、間にいくつかのジャンプがあることはわかっています。 If-Else、の場合は行を繰り返す ながら/のために ループ、を使用して特定の行に移動する ジャンプ 声明などこれらのジャンプに基づいて、手続き型パラダイムには次の 2 つのサブセットがあります。 構造化された パラダイム& 非構造化 パラダイム。

非構造化パラダイム

C、BASIC、Assembly などの特定の言語では、特定のステートメント (GoTo など) を使用して、実行のために任意の行にジャンプできます。 特に Edsger W. Dijkstra による批判の後では、プログラムのテストとサブモジュールへの分割が難しくなるため、これは最近では良い方法ではないと考えられています。

// Code
goto label x; // Skip statement 1 & 2 and start from label x
statement 1;
Statement 2;
label x;statement 3;
statement 4;
// Code

構造化パラダイム

これは、広く使用されているパラダイムの 1 つであり、プログラムは以下を使用して記述されます。 順序選択、 と 反復. シーケンスは、シーケンス内のステートメントを 1 つずつ順番に実行する方法です。 選択は、必要な部分だけが実行される If-Else ステートメントを使用して実現されます。 最後に、条件が満たされるまでいくつかの行が繰り返される For/While ループを使用して反復が実現されます。

// Codeif(test) {
howMany = 10;
} else {
howMany = 100;
}
// Print students' name
for(let i = 1; i <= howMany; i++) {
console.log(students[i].name)
}

構造化プログラミングは、任意のプログラミング言語、特に手続き型プログラミング言語で実行できます。これは、ほとんどの言語に上記の必要な制御構造が含まれているためです。

オブジェクト指向パラダイム

これは最も有名なものですが、オブジェクト指向パラダイムとは何かについて明確な定義がないため、理解するのが少し複雑です。 オブジェクト指向プログラミングが以下の機能を備えているという最も一般的な定義を考えてみましょう。 カプセル化継承、 と ポリモーフィズム. オブジェクト指向プログラミングについて詳しく説明しますが、この記事を短くするためにここでは簡単に説明します。

手続き型プログラミングではデータと関数が分離されていますが、オブジェクト指向ではこれらを外界から分離されたオブジェクトに結合します。この概念はカプセル化として知られています。 継承とは、すべてをゼロから行うのではなく、既存の定義を拡張して特定の機能を再利用することを意味します。 最後に、最も強力な機能であるポリモーフィズムは、単一のインターフェイスを使用して実行時にさまざまな動作を呼び出す機能です。 オブジェクト指向のパラダイムに慣れていない場合、これらを理解するのは難しいかもしれませんが、別の記事で詳しく説明します。

// Language: Typescript// Encapsulation & Inheritance
interface Student {
name: string;
getName: () => string;
}
class MasterStudent implements Student {
name: string;
constructor(name: string) {
this.name = name;
}
getName(): string {
return this.name;
}
}
// Polymorphism
const masterStudent: Student = new MasterStudent("Mark");

現時点では、プログラミング言語に上記の機能がある場合、オブジェクト指向のパラダイムに従っていることを知っておく必要があります。 しかし、完全ではありませんが、C などの手続き型言語でこれらすべてを行うことができるため、これは純粋に真実でもありません (これが、混乱を招く可能性があると私が言った理由です)。 オブジェクト指向言語は、これらのことをほとんど完璧な方法で行うための機能を提供します。

関数型プログラミングである単一のパラダイムについて簡単に説明する以外は、このパラダイムについてはあまり説明しません。 最近人気が高まっており、多くの言語をサポートしています。

関数型プログラミングのパラダイム

一部の人々は、関数型プログラミングがエラーのないコードを書くのに最適な方法であると信じています。 この言語は、関数型プログラミングをサポートするために、関数を第一級市民として扱う必要があります。 第一級市民とは、関数に値を割り当て、引数として渡し、関数から返すことができることを意味します。

関数型プログラミングの要点は、データの変更が許可されていない不変性です。 変更が必要な場合は、非常に制限された条件下でのみ変更できます。 状態を変更せず、同じ引数に対して同じ結果を返す純粋関数と呼ばれるものを作成します。

// Sum will always return the same result for same arguments
const sum = (a, b) => a + b

コンポジションを使用して、さまざまな機能を組み合わせることもできます。

const firstClass = (students) => students.filter(s => s.marks > 90)
const getNames = (students) => students.map(s => s.name)
// Code to get students
const firstClassStudentNames = getNames(firstClass(students))

しかし、なぜそれが宣言的パラダイムに分類されるのでしょうか? フローを書く必要がなく、何をするかを指定するだけだからです。 次の例では、宣言型プログラミングでそれを行う方法を示すのではなく、何を行うかを示しているだけです。

// Declarative
const sumDeclarative = (list) => list.reduce((acc, current) => acc + current);
// Imperative
const sumImperative = (list) => {
let sum = 0;
for(let i = 0; i < list.length; i++) {
sum += list[i];
}
return sum;
}

完了です! これが長い記事であることは承知していますが、ここまでたどり着いたのであれば、おそらく何か役に立つことを学んだに違いありません。 よろしければ、購読、拍手、共有して、もっと書く動機を与えてください.

ありがとうございました!

購読して、次の記事とビデオにご期待ください。

Leave a Comment

Your email address will not be published. Required fields are marked *