出典: The Geek Asian オブジェクト指向プログラミングの 4 つの基本を見て、それらがどのように機能するかを理解してみましょう。オブジェクト指向プログラミング (OOP) は、主要なプログラミング パラダイムの 1 つです。それは簡単でシンプルなこともあれば、逆に非常に複雑なこともあります。それはすべて、アプリケーションをどのように開発するかによって決まります。 OOP には 4 つの柱があります。
- カプセル化。
- 継承。
- 抽象化。
- 多態性。
1. カプセル化
私たちは皆、データ要素を隠し、ユーザーがパブリック メソッドを使用してデータにアクセスできるようにするカプセル化について研究したことがあります。これらをゲッターとセッターと呼びます。さて、このことは忘れて、もっと簡単な定義を見つけてみましょう。カプセル化は、データの整合性を維持するために、ユーザーがデータ メンバーやクラス変数を直接変更できないように制限する方法です。どうやってこれを行うのでしょうか?アクセス修飾子をプライベートに切り替え、データへのアクセスに使用できるパブリック メソッドを公開することで、変数へのアクセスを制限します。以下に具体的な例を見てみましょう。これは、カプセル化を使用してデータの整合性を維持する方法を理解するのに役立ちます。カプセル化なし:/**
* @author thegeekyasian.com
*/
public class Account {
public double balance;
public static void main(String[] args) {
Account theGeekyAsianAccount = new Account();
theGeekyAsianAccount.balance = -54;
}
}
上記のコード スニペットでは、main()メソッドがバランス変数に直接アクセスします。これにより、ユーザーはAccountクラスのBalance変数に任意の double 値を設定できるようになります。誰でも残高を無効な数値 (この場合は -54 など)に設定できるようにすると、データの整合性が失われる可能性があります。カプセル化の場合:
/**
* @author thegeekyasian.com
*/
public class Account {
private double balance;
public void setBalance(double balance) {
if(balance >= 0) { // Validating input data in order to maintain data integrity
this.balance = balance;
}
throw new IllegalArgumentException("Balance cannot be less than zero (0)");
}
public static void main(String[] args) {
Account theGeekyAsianAccount = new Account();
theGeekyAsianAccount.setBalance(1); // Valid input - Allowed
theGeekyAsianAccount.setBalance(-55); // Stops user and throws exception
}
}
このコードでは、balance変数へのアクセスを制限し、ユーザーがAccountの残高値を設定できるようにするsetBalance()メソッドを追加しました。セッターは、変数に割り当てる前に、提供された値をチェックします。値がゼロより小さい場合、例外がスローされます。これにより、データの整合性が損なわれないことが保証されます。上記の例を説明した後、OOP の 4 つの柱の 1 つとしてのカプセル化の価値が明確になったと思います。
2. 継承
継承は、共通の機能を共有する別のクラスのプロパティを取得する方法です。これにより、再利用性が向上し、コードの重複が削減されます。このメソッドには、子要素が親のプロパティを継承するときの、子要素と親要素の相互作用の原理もあります。2 つの簡単な例を見て、継承によってコードがどのようにシンプルになり再利用可能になるかを見てみましょう。継承なし:/**
* @author thegeekyasian
*/
public class Rectangle {
private int width;
private int height;
public Rectangle(int width, int height) {
this.width = width;
this.height = height;
}
public int getArea() {
return width * height;
}
}
public class Square {
private int width; // Duplicate property, also used in class Rectangle
public Square(int width) {
this.width = width;
}
public int getArea() { // Duplicate method, similar to the class Rectangle
return this.width * this.width;
}
}
2 つの類似したクラスは、widthプロパティとgetArea()メソッドを共有します。SquareクラスがRectangleクラスから継承するような小さなリファクタリングを行うことで、コードの再利用を増やすことができます。継承の場合:
/**
* @author thegeekyasian
*/
public class Rectangle {
private int width;
private int height;
public Rectangle(int width, int height) {
this.width = width;
this.height = height;
}
public int getArea() {
return width * height;
}
}
public class Square extends Rectangle {
public Square(int width) {
super(width, width); // A rectangle with the same height as width is a square
}
}
Rectangleクラス を拡張するだけで、 SquareクラスがRectangle型として得られます。これは、 SquareとRectangleに共通するすべてのプロパティを継承することを意味します。上記の例では、コードを再利用可能にする上で継承がどのように重要な役割を果たしているかがわかります。また、クラスがその親クラスの動作を継承することもできます。
3. 抽象化
抽象化とは、オブジェクトの不必要または無関係な詳細を非表示にすることで、重要な詳細のみをユーザーに提示する手法です。ユーザー側の操作の複雑さを軽減するのに役立ちます。抽象化により、アクションを実行するために複雑な詳細を要求することなく、シンプルなインターフェイスをユーザーに提供できます。簡単に言えば、ユーザーはエンジンがどのように動作するかを正確に理解する必要なく、車を運転できるようになります。まず例を見てから、抽象化がどのように役立つかについて説明します。/**
* @author thegeekyasian.com
*/
public class Car {
public void lock() {}
public void unlock() {}
public void startCar() {
checkFuel();
checkBattery();
whatHappensWhenTheCarStarts();
}
private void checkFuel() {
// Check fuel level
}
private void checkBattery() {
// Check car battery
}
private void whatHappensWhenTheCarStarts() {
// Magic happens here
}
}
上記のコードでは、 lock()、unlock()、およびstartCar() メソッドはパブリックであり、残りはクラスに対してプライベートです。ユーザーが「クルマを運転する」ことをより簡単にしました。もちろん、startCar() で車を始動する前にcheckFuel()とcheckBattery()を手動でチェックすることもできますが、それはプロセスを複雑にするだけです。上記のコードでは、ユーザーが行う必要があるのはstartCar()を使用することだけで、残りはクラスが処理します。これが抽象化と呼ばれるものです。
4. ポリモーフィズム
OOP の 4 つの柱の最後で最も重要なのはポリモーフィズムです。ポリモーフィズムとは「多くの形式」を意味します。名前が示すように、これはアクションを複数または異なる方法で実行できるようにする機能です。ポリモーフィズムについて話すとき、その型について話さない限り、議論することはあまりありません。ポリモーフィズムには 2 つのタイプがあります。- メソッドのオーバーロード - 静的多態性 (静的バインディング)。
- メソッドのオーバーライド - 動的ポリモーフィズム (動的バインディング)。
メソッドのオーバーロード - 静的多態性:
メソッドのオーバーロードまたは静的ポリモーフィズムは、静的バインディングまたはコンパイル時バインディングとも呼ばれ、メソッド呼び出しがコンパイル時に決定されるタイプです。メソッドのオーバーロードを使用すると、同じ名前、異なるパラメーター データ型、異なる数のパラメーター、またはその両方を持つ複数のメソッドを使用できます。しかし問題は、メソッドのオーバーロード (または静的ポリモーフィズム) がなぜ役立つのかということです。メソッドのオーバーロードをよりよく理解するために、以下の例を見てみましょう。メソッドのオーバーロードなし:/**
* @author thegeekyasian.com
*/
public class Number {
public void sumInt(int a, int b) {
System.out.println("Sum: " + (a + b));
}
public void sumDouble(double a, double b) {
System.out.println("Sum: " + (a + b));
}
public static void main(String[] args) {
Number number = new Number();
number.sumInt(1, 2);
number.sumDouble(1.8, 2.5);
}
}
上の例では、2 つの異なるタイプの数値を追加するためだけに、異なる名前の 2 つのメソッドを作成しました。同様の実装を続けると、異なる名前のメソッドが複数存在することになります。これにより、コードの品質と可用性が低下します。これを改善するには、異なるメソッドに同じ名前を使用するメソッドのオーバーロードを使用します。これにより、ユーザーはさまざまな種類の数値を合計するためのエントリ ポイントとして 1 つのオプションを使用できるようになります。メソッドのオーバーロードは、2 つ以上のメソッドの名前が同じでパラメータが異なる場合に機能します。戻り値の型は同じでも異なっていても構いません。ただし、2 つのメソッドの名前、パラメータが同じで、戻り値の型が異なる場合、オーバーロードが発生し、コンパイル エラーが発生します。メソッドのオーバーロードを使用すると、次のようになります。
/**
* @author thegeekyasian.com
*/
public class Number {
public void sum(int a, int b) {
System.out.println("Sum: " + (a + b));
}
public void sum(double a, double b) {
System.out.println("Sum: " + (a + b));
}
public static void main(String[] args) {
Number number = new Number();
number.sum(1, 2);
number.sum(1.8, 2.5);
}
}
同じコードで、いくつかの小さな変更を加えて、両方のメソッドをオーバーロードし、両方の名前を同じにすることができました。ユーザーは、メソッドのパラメーターとして特定のデータ型を指定できるようになりました。次に、提供されたデータ型に基づいてアクションを実行します。コンパイラは、指定されたパラメータ型でどのメソッドが呼び出されるかを認識しているため、このメソッド バインディングはコンパイル時に行われます。これが、これをコンパイル時バインディングと呼ぶ理由です。
メソッドのオーバーライド - 動的多態性:
メソッドのオーバーロードとは異なり、メソッドのオーバーライドでは複数のメソッドとまったく同じシグネチャを持つことができますが、それらのシグネチャは複数の異なるクラスに存在する必要があります。問題は、何がそんなに特別なのかということです。これらのクラスには IS-A 関係があります。つまり、相互に継承する必要があります。つまり、メソッドのオーバーライドまたは動的ポリモーフィズムでは、メソッドが呼び出されたときに実行時にメソッドが動的に処理されます。これは、初期化に使用されたオブジェクトへの参照に基づいて行われます。以下はメソッドのオーバーライドの小さな例です。/**
* @author thegeekyasian.com
*/
public class Animal {
public void walk() {
System.out.println("Animal walks");
}
}
public class Cat extends Animal {
@Override
public void walk() {
System.out.println("Cat walks");
}
}
public class Dog extends Animal {
@Override
public void walk() {
System.out.println("Dog walks");
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Animal();
animal.walk(); // Animal walks
Cat cat = new Cat();
cat.walk(); // Cat walks
Dog dog = new Dog();
dog.walk(); // Dog walks
Animal animalCat = new Cat(); // Dynamic Polymorphism
animalCat.walk(); // Cat walks
Animal animalDog = new Dog(); // Dynamic Polymorphism
animalDog.walk(); //Dog walks
}
}
このオーバーライド例では、タイプ「Dog」と「Cat」のオブジェクトをタイプ「Animal」に動的に割り当てました。これにより、参照されたインスタンスのwalk()メソッドを実行時に動的に呼び出すことができます。これは、メソッドのオーバーライド (または動的ポリモーフィズム) を使用して行うことができます。これで、OOP の 4 つの柱に関する簡単な説明が終わりました。お役に立てば幸いです。
GO TO FULL VERSION