Hi các bạn, mình là TUẤN. Hiện đang là một Full-stack Web Developer tại Tokyo😊. Hãy follow blog của mình để không bỏ lỡ những bài viết hữu ích và thú vị trong tương lại nhé.
Nếu bạn thích bài viết, xin hãy cho mình 1 upvote và đăng ký để ủng hộ mình có thêm động lực ra thêm nhiều bài viết hay hơn trong tương lại nhé.😊
Ae nào muốn làm
Dev
hoặcBrSE
tại Nhật (N3-N4, 1-2 năm exp trở lên hoặc zero tech có tiếng N2-N3, cả 2 đầu Nhật và VN) cần mình đưa roadmap hoặc review CV, hoặc muốn hiểu hơn về các câu hỏi thường gặp khi interviewDev
hoặcBrSE
, cách deal lương cao... cần support thì cứ liên hệ mình qua zalo nhé: 0379302361 hoặc Facebook của mình. free
Đợt này nghiện ngập game zelda totk quá ko có time viết bài cho anh em. Hôm nay lại có ông em hỏi về Class và Interface mình biết đây là một topic cũ mèm biết rồi khổ lắm nói mãi. Tuy nhiên lỡ đâu có ae nào vẫn còn lờ mờ thì xin được viết vài dòng để giới thiệu qua vấn đề này. Vì đang trong quá trình nghiệm ngập zelda tears of the kingdom nên mình cũng sẽ sử dụng ví dụ mô phỏng game này luôn nhé.
1. Giới thiệu:
1.1. Định nghĩa về Interface
Để bắt đầu, hãy cùng mình hiểu về khái niệm "Interface" nào. Trong lập trình hướng đối tượng (OOP), Interface đóng vai trò như một khuôn mẫu, định nghĩa ra một tập các phương thức mà các Class khác cần tuân theo nếu muốn implement (thực thi) Interface đó. Nói cách khác, Interface là một khuôn mẫu hoàn hảo để đảm bảo mọi thứ đều hoạt động như mong đợi.
1.2. Định nghĩa về Class
Class, một khái niệm quen thuộc đối với mọi lập trình viên. Class đại diện cho một nhóm các đối tượng có các đặc điểm chung. Class định nghĩa ra các đặc tính (fields) và hành động (methods) mà một đối tượng của nó sẽ có. Bạn có thể hiểu Class là một bản thiết kế, còn đối tượng (instance) là một thực thể của bản thiết kế đó.
2. Sự khác nhau giữa Interface và Class
2.1. Điểm giống nhau
Cả Interface và Class đều định nghĩa ra cấu trúc của một "thực thể" nào đó. Chúng đều chứa các thuộc tính và phương thức cần thiết.
2.2. Điểm khác biệt
Tuy có điểm giống nhau nhưng chúng có sự khác biệt đáng kể. Interface chỉ định nghĩa ra cấu trúc nhưng không có thể hiện (implementation). Trong khi đó, Class lại định nghĩa cả cấu trúc và cách thể hiện của chúng.
3. Đa kế thừa và Interface
3.1. Vì sao class chỉ extend được 1 class mà implement nhiều interface
Đây là câu hỏi mà không ít người hỏi mình. Trong các ngôn ngữ lập trình hướng đối tượng, một Class chỉ có thể kế thừa từ một Class khác (Single Inheritance). Tuy nhiên, một Class có thể implement nhiều Interface. Nguyên nhân chính của việc hạn chế này là để tránh "Diamond Problem" - một vấn đề đặc biệt phát sinh khi một class kế thừa từ hai class khác và cả hai đều có cùng một phương thức.
Tưởng tượng bạn có hai class, Class A và Class B, cả hai đều có phương thức print()
. Class C kế thừa từ cả Class A và Class B. Khi bạn gọi phương thức print()
từ một đối tượng của Class C, ngôn ngữ lập trình sẽ không biết phương thức print()
nào nên được thực hiện - phương thức từ Class A hay Class B? Đây là Diamond Problem.
class A { void print() { System.out.println("A"); }
} class B { void print() { System.out.println("B"); }
} // Không thể làm như sau trong Java bởi vì Java không hỗ trợ đa kế thừa
class C extends A, B { // Which print method should we call?
}
Để giải quyết vấn đề này, Java hỗ trợ đa kế thừa thông qua việc sử dụng Interface. Một Interface là một khung xương hoặc hợp đồng mà tất cả các class implement nó phải tuân theo. Một class có thể implement nhiều interface, vì vậy ta có thể tận dụng khả năng này để tạo ra một hình thức đa kế thừa.
interface PrintableA { default void print() { System.out.println("A"); }
} interface PrintableB { default void print() { System.out.println("B"); }
} class C implements PrintableA, PrintableB { // Đối mặt với một mâu thuẫn: phải gọi print() từ PrintableA hay PrintableB? // Java yêu cầu chúng ta phải xử lý mâu thuẫn này @Override public void print() { // Tùy bạn chọn print() nào để sử dụng, hoặc bạn có thể kết hợp cả hai PrintableA.super.print(); PrintableB.super.print(); }
}
3.2. Ưu và nhược điểm của việc sử dụng Interface trong việc kế thừa
Điều này đem lại lợi ích đó là một Class có thể kế thừa nhiều hành vi từ các Interface khác nhau, mở rộng khả năng mô phỏng thế giới thực. Tuy nhiên, cũng có nhược điểm là phải thực hiện tất cả các phương thức của Interface.
4. Sự kế thừa trong Class và Interface
4.1. Class con extend class cha không cần implement vẫn ok
Một điều thú vị nữa, nếu Class cha đã implement một Interface, thì Class con của nó khi extend Class cha không cần phải implement lại Interface đó. Nhưng nếu Class con muốn thay đổi hành vi của phương thức đã được Class cha định nghĩa, nó có thể override phương thức đó.
4.2. Ví dụ minh họa
Giả sử, chúng ta có Interface "Edible" với phương thức "eat". Class "Fruit" implement "Edible" và định nghĩa phương thức "eat". Bây giờ, Class "Apple" extend "Fruit" không cần phải implement lại "Edible" nữa. Nếu "Apple" muốn ăn khác đi, nó có thể override phương thức "eat".
5. Ví dụ zui zui
Hãy tưởng tượng bạn đang phát triển game RPG zelda tears of the kingdom. Trong game, chúng ta sẽ có nhiều nhân vật khác nhau như nhân vật chính, kẻ thù, nhân vật phụ (NPC), v.v., và mỗi nhân vật sẽ có các chức năng khác nhau.
5.1. Định nghĩa các Interface
Trước hết, chúng ta sẽ định nghĩa các Interfaces để xác định các hành vi cần thiết cho các đối tượng trong trò chơi:
// Tạo Interface Movable định nghĩa hành vi di chuyển
interface Movable { position: { x: number; y: number }; move: (x: number, y: number) => void;
} // Tạo Interface Damageable để xác định những đối tượng có thể bị tấn công
interface Damageable { health: number; takeDamage: (damage: number) => void;
} // Interface Fighter kế thừa từ Damageable và bổ sung hành vi tấn công
interface Fighter extends Damageable { attackPower: number; attack: (target: Damageable) => void;
}
5.2. Tạo Class Character
Tiếp theo, chúng ta sẽ tạo lớp cơ bản Character
cho tất cả các nhân vật trong game. Nhân vật sẽ có khả năng di chuyển và có thể chịu sát thương:
class Character implements Movable, Damageable { position = { x: 0, y: 0 }; health: number; constructor(x: number, y: number, health: number) { this.position.x = x; this.position.y = y; this.health = health; } move(x: number, y: number) { this.position.x = x; this.position.y = y; console.log(`Moved to (${this.position.x}, ${this.position.y})`); } takeDamage(damage: number) { this.health -= damage; console.log(`Took ${damage} damage. Current health: ${this.health}`); }
}
5.3. Tạo Class Hero và Class Enemy
Cuối cùng, chúng ta sẽ tạo lớp Hero
và Enemy
. Cả hai đều là các Character
, nhưng Hero
và Enemy
còn có thể tấn công:
class Hero extends Character implements Fighter { attackPower: number; constructor(x: number, y: number, health: number, attackPower: number) { super(x, y, health); this.attackPower = attackPower; } attack(target: Damageable) { console.log(`Attacked enemy for ${this.attackPower} damage`); target.takeDamage(this.attackPower); }
} class Enemy extends Character implements Fighter { attackPower: number; constructor(x: number, y: number, health: number, attackPower: number) { super(x, y, health); this.attackPower = attackPower; } attack(target: Damageable) { console.log(`Attacked hero for ${this.attackPower} damage`); target.takeDamage(this.attackPower); }
}
Với những lớp và giao diện này, bạn có thể mô phỏng các trận đánh giữa hero và enemy, cũng như việc di chuyển của các nhân vật. Bạn có thể mở rộng thêm bằng cách thêm nhiều hành động và thuộc tính khác nhau để làm cho trò chơi của mình trở nên thú vị và phong phú hơn.
6. Kết luận và Lưu ý
Mình đã giới thiệu và giải thích qua về các khái niệm quan trọng trong lập trình hướng đối tượng, bao gồm class, interface. Tuy nhiên, cần lưu ý rằng việc lựa chọn khi nào dùng class, khi nào dùng interface, cũng như quyết định về việc sử dụng kế thừa và đa hình đều phụ thuộc vào từng bài toán cụ thể trong thực tế. Việc sử dụng linh hoạt các khái niệm này sẽ giúp cho mã nguồn của bạn trở nên dễ hiểu, dễ quản lý hơn.
7. Các câu hỏi thường gặp
-
Có phải class luôn luôn cần có constructor không? Không, constructor không phải lúc nào cũng cần thiết. Tuy nhiên, nếu bạn muốn khởi tạo một số giá trị cho đối tượng khi nó được tạo, thì constructor là nơi thích hợp để làm điều đó.
-
Tôi có thể implement nhiều interface cho một class được không? Có, một class có thể implement nhiều interface. Điều này hữu ích khi bạn muốn đối tượng của mình có nhiều hành vi khác nhau.
-
Khi nào tôi nên sử dụng inheritance (kế thừa)? Bạn nên sử dụng kế thừa khi có mối quan hệ "is-a" (là một) giữa hai class. Ví dụ, nếu
Hero
là mộtCharacter
, thì bạn có thể sử dụng kế thừa. -
Có thể override (ghi đè) phương thức của class cha không? Có, bạn hoàn toàn có thể override phương thức của class cha. Điều này hữu ích khi bạn muốn thay đổi hoặc mở rộng hành vi của phương thức đó trong class con.
-
Khi nào tôi nên sử dụng polymorphism (đa hình)? Bạn nên sử dụng đa hình khi bạn muốn thực hiện một hành động cụ thể nhưng có thể có nhiều cách thực hiện khác nhau tùy thuộc vào đối tượng đang thực hiện hành động đó. Ví dụ, trong trường hợp của chúng ta, mỗi
Character
đều có hành viattack()
, nhưng cách thực hiện cụ thể sẽ khác nhau tùy thuộc vào đối tượng đang tấn công. -
Tại sao lại cần sử dụng interface? Interface giúp định rõ cấu trúc mà các class khác cần tuân theo, giúp đảm bảo tính nhất quán trong mã nguồn. Thông qua việc implement interface, chúng ta có thể đảm bảo rằng một class cụ thể sẽ cung cấp những phương thức nào.
-
Tôi có thể tạo một đối tượng từ một interface không? Không, bạn không thể tạo một đối tượng từ một interface. Interface chỉ định rõ "giao diện" mà một class nào đó cần phải tuân theo, nhưng không cung cấp các phương thức cụ thể.
-
Khi nào nên sử dụng class trừu tượng (abstract class)? Class trừu tượng (abstract class) hữu ích khi bạn muốn tạo ra một class cơ sở mà có một số phương thức đã được định nghĩa sẵn, nhưng cũng muốn đảm bảo rằng một số phương thức khác phải được định nghĩa trong các class con.
English version
This time I'm addicted to the game Zelda: Tears of the Kingdom, so I don't have time to write an article for you guys. But today, one of my friends asked me about Classes and Interfaces, and although it's an old topic, I know it can be confusing for some people. So, if there's anyone still unsure about it, I'd be happy to write a few lines to introduce the topic. Since I'm currently immersed in the world of Zelda: Tears of the Kingdom, I'll use examples from the game to explain.
1. Introduction:
1.1. Definition of Interface
To start, let's understand the concept of "Interface." In object-oriented programming (OOP), an Interface serves as a blueprint, defining a set of methods that other Classes must follow if they want to implement that Interface. In other words, an Interface is a perfect blueprint to ensure that everything works as expected.
1.2. Definition of Class
Class, a familiar concept for every programmer. A Class represents a group of objects that share common characteristics. It defines the attributes (fields) and actions (methods) that an object of that Class will have. You can think of a Class as a blueprint, and an object (instance) as an entity created from that blueprint.
2. Differences between Interface and Class
2.1. Similarities
Both Interface and Class define the structure of an "entity." They both contain necessary attributes and methods.
2.2. Differences
Although they share similarities, there are significant differences between them. An Interface only defines the structure but does not provide an implementation. On the other hand, a Class defines both the structure and the way it is implemented.
3. Multiple Inheritance and Interface
3.1. Why can a class extend only one class but implement multiple interfaces?
This is a question that many people ask me. In object-oriented programming languages, a Class can only inherit from one other Class (Single Inheritance). However, a Class can implement multiple Interfaces. The main reason for this limitation is to avoid the "Diamond Problem" - a particular problem that arises when a Class inherits from two other Classes, and both have the same method.
Imagine you have two classes, Class A and Class B, both of which have a method called print()
. Class C inherits from both Class A and Class B. When you call the print()
method from an object of Class C, the programming language doesn't know which print()
method should be executed - the one from Class A or Class B? This is the Diamond Problem.
class A { void print() { System.out.println("A"); }
} class B { void print() { System.out.println("B"); }
} // This cannot be done in Java because Java does not support multiple inheritance
class C extends A, B { // Which print method should we call?
}
To solve this problem, Java supports multiple inheritance through the use of Interfaces. An Interface is a framework or contract that all implementing classes must adhere to. A class can implement multiple interfaces, so we can leverage this capability to achieve a form of multiple inheritance.
interface PrintableA { default void print() { System.out.println("A"); }
} interface PrintableB { default void print() { System.out.println("B"); }
} class C implements PrintableA, PrintableB { // Faced with a conflict: which print() to call from PrintableA or PrintableB? // Java requires us to handle this conflict @Override public void print() { // You can choose which print() to use, or you can combine both PrintableA.super.print(); PrintableB.super.print(); }
}
3.2. Advantages and disadvantages of using Interface for inheritance
The advantage is that a Class can inherit multiple behaviors from different Interfaces, expanding the ability to simulate the real world. However, the disadvantage is that all methods of the Interface must be implemented.
4. Inheritance in Class and Interface
4.1. Subclass extending a superclass does not need to implement the interface
Another interesting thing is that if a superclass has already implemented an Interface, then its subclass extending the superclass does not need to implement that Interface again. However, if the subclass wants to change the behavior of a method defined by the superclass, it can override that method.
4.2. Illustrative example
Let's assume that we have an Interface called "Edible" with an "eat" method. The "Fruit" Class implements "Edible" and defines the "eat" method. Now, the "Apple" Class extends "Fruit" and does not need to implement "Edible" again. If the "Apple" wants to eat differently, it can override the "eat" method.
5. Fun example
Imagine you're developing an RPG game called Zelda: Tears of the Kingdom. In the game, we'll have different characters like the main character, enemies, NPCs, etc., and each character will have different functionalities.
5.1. Defining Interfaces
First, let's define the Interfaces to specify the necessary behaviors for objects in the game:
// Create the Movable Interface defining the move behavior
interface Movable { position: { x: number; y: number }; move: (x: number, y: number) => void;
} // Create the Damageable Interface to identify objects that can be attacked
interface Damageable { health: number; takeDamage: (damage: number) => void;
} // The Fighter Interface extends Damageable and adds the attack behavior
interface Fighter extends Damageable { attackPower: number; attack: (target: Damageable) => void;
}
5.2. Creating the Character Class
Next, let's create a basic Character
Class for all characters in the game. Characters will have the ability to move and can be damaged:
class Character implements Movable, Damageable { position = { x: 0, y: 0 }; health: number; constructor(x: number, y: number, health: number) { this.position.x = x; this.position.y = y; this.health = health; } move(x: number, y: number) { this.position.x = x; this.position.y = y; console.log(`Moved to (${this.position.x}, ${this.position.y})`); } takeDamage(damage: number) { this.health -= damage; console.log(`Took ${damage} damage. Current health: ${this.health}`); }
}
5.3. Creating the Hero and Enemy Classes
Finally, let's create the Hero
and Enemy
classes. Both are Character
, but they can also attack:
class Hero extends Character implements Fighter { attackPower: number; constructor(x: number, y: number, health: number, attackPower: number) { super(x, y, health); this.attackPower = attackPower; } attack(target: Damageable) { console.log(`Attacked enemy for ${this.attackPower} damage`); target.takeDamage(this.attackPower); }
} class Enemy extends Character implements Fighter { attackPower: number; constructor(x: number, y: number, health: number, attackPower: number) { super(x, y, health); this.attackPower = attackPower; } attack(target: Damageable) { console.log(`Attacked hero for ${this.attackPower} damage`); target.takeDamage(this.attackPower); }
}
With these classes and interfaces, you can simulate battles between the hero and enemies, as well as the movement of the characters. You can expand further by adding more actions and attributes to make your game more interesting and diverse.
6. Conclusion and Notes
I have introduced and explained important concepts in object-oriented programming, including classes and interfaces. However, it's important to note that the choice of when to use a class or an interface, as well as the decision to use inheritance and polymorphism, depends on specific problems in practice. Using these concepts flexibly will make your source code more understandable and manageable.
7. Frequently Asked Questions
-
Do classes always need constructors? No, constructors are not always necessary. However, if you want to initialize certain values for an object when it is created, then a constructor is a suitable place to do that.
-
Can I implement multiple interfaces for a class? Yes, a class can implement multiple interfaces. This is useful when you want your object to have multiple behaviors.
-
When should I use inheritance? You should use inheritance when there is an "is-a" relationship between two classes. For example, if a
Hero
is aCharacter
, then you can use inheritance. -
Can I override methods of the superclass? Yes, you can override methods of the superclass. This is useful when you want to modify or extend the behavior of that method in the subclass.
-
When should I use polymorphism? You should use polymorphism when you want to perform a specific action but there can be different ways to perform it depending on the object performing the action. For example, in our case, each
Character
has anattack()
behavior, but the specific implementation will vary depending on the attacking object. -
Why should I use interfaces? Interfaces help define the structure that other classes must adhere to, ensuring consistency in the source code. By implementing an interface, we can guarantee that a specific class will provide the required methods.
-
Can I create an object from an interface? No, you cannot create an object directly from an interface. An interface only defines the "interface" that a class must adhere to, but it does not provide concrete methods.
-
When should I use an abstract class? An abstract class is useful when you want to create a base class that has some predefined methods but also wants to ensure that some methods must be defined in the subclass.
日本語版
この度、私はゼルダの伝説ティアーズオブザキングダムというゲームに夢中になっており、皆さんに記事を書く時間がありません。しかし、今日は友人の一人がクラスとインターフェースについて尋ねてきました。古いトピックではありますが、まだ理解しきれていない人もいるかもしれないので、少し紹介するための文章を書くことにしました。現在、私はゼルダの伝説ティアーズオブザキングダムの世界に没頭しているため、ゲームからの例を使って説明します。
1. 導入:
1.1. インターフェースの定義
まず、"インターフェース"という概念を理解しましょう。オブジェクト指向プログラミング(OOP)では、インターフェースは他のクラスがそのインターフェースを実装する場合に従う必要のある一連のメソッドを定義する設計図として機能します。言い換えると、インターフェースはすべてが予想どおりに動作することを保証する完璧な設計図です。
1.2. クラスの定義
プログラマにとっては馴染みのある概念であるクラスは、共通の特徴を持つオブジェクトのグループを表します。クラスは、そのクラスのオブジェクトが持つ属性(フィールド)とアクション(メソッド)を定義します。クラスは設計図として考えることができ、オブジェクト(インスタンス)はその設計図から作成される実体です。
2. インターフェースとクラスの違い
2.1. 類似点
インターフェースとクラスの両方が「エンティティ」の構造を定義します。両方とも必要な属性とメソッドを含んでいます。
2.2. 違い
共通点はあるものの、インターフェースとクラスには重要な違いがあります。インターフェースは構造を定義するだけであり、実装は提供しません。一方、クラスは構造とその実装方法の両方を定義します。
3. 複数の継承とインターフェース
3.1. なぜクラスは1つしか継承できず、インターフェースは複数実装できるのですか?
これは多くの人が私に尋ねる質問です。オブジェクト指向プログラミング言語では、クラスは他のクラスからのみ継承できます(単一継承)。しかし、クラスは複数のインターフェースを実装できます。この制限の主な理由は、「ダイヤモンド問題」と呼ばれる特定の問題を避けるためです。この問題は、1つのクラスが2つの他のクラスから継承し、両方に同じメソッドがある場合に発生します。
クラスAとクラスBという2つのクラスがあり、それぞれに「print()」というメソッドがあると想像してください。クラスCはクラスAとクラスBの両方から継承しています。クラスCのオブジェクトから「print()」メソッドを呼び出すとき、プログラミング言語はどの「print()」メソッドを実行すべきかわかりません。クラスAのものを使うべきか、クラスBのものを使うべきか?これがダイヤモンド問題です。
class A { void print() { System.out.println("A"); }
} class B { void print() { System.out.println("B"); }
} // Javaではこれはできません。Javaは複数の継承をサポートしていません。
class C extends A, B { // どのprintメソッドを呼び出すべきですか?
}
この問題を解決するために、Javaはインターフェースを通じて複数の継承をサポートしています。インターフェースは、すべての実装クラスが従うべきフレームワークや契約です。クラスは複数のインターフェースを実装できるため、この機能を活用して一種の複数の継承を実現することができます。
interface PrintableA { default void print() { System.out.println("A"); }
} interface PrintableB { default void print() { System.out.println("B"); }
} class C implements PrintableA, PrintableB { // 衝突が発生しています: PrintableAのprint()とPrintableBのprint()のどちらを呼び出すべきですか? // Javaはこの衝突を解決する必要があります。 @Override public void print() { // 使用するprint()を選択するか、両方を組み合わせることができます。 PrintableA.super.print(); PrintableB.super.print(); }
}
3.2. インターフェースを使用した継承の利点と欠点
利点は、クラスがさまざまなインターフェースから複数の振る舞いを継承できることで、現実世界をシミュレートする能力が広がります。ただし、欠点はインターフェースのすべてのメソッドを実装しなければならないということです。
4. クラスとインターフェースにおける継承
4.1. スーパークラスを拡張するサブクラスはインターフェースを実装する必要はありません
もう一つ興味深いことは、スーパークラスがすでにインターフェースを実装している場合、スーパークラスを拡張するサブクラスはそのインターフェースを再度実装する必要がないということです。ただし、サブクラスがスーパークラスで定義されたメソッドの動作を変更したい場合は、そのメソッドをオーバーライドすることができます。
4.2. 説明的な例
「食べる(eat)」メソッドを持つ「Edible」というインターフェースがあるとします。クラス「Fruit」は「Edible」を実装し、「eat」メソッドを定義します。そして、「Apple」クラスは「Fruit」を拡張し、「Edible」を再度実装する必要はありません。ただし、「Apple」が異なる方法で食べたい場合は、「eat」メソッドをオーバーライドすることができます。
5. 楽しい例
「ゼルダの伝説ティアーズオブザキングダム」というRPGゲームを開発していると想像してみてください。ゲーム内には主人公、敵キャラクター、NPCなど、さまざまなキャラクターが存在し、それぞれが異なる機能を持ちます。
5.1. インターフェースの定義
まず、ゲーム内のオブジェクトに必要な振る舞いを指定するために、次のインターフェースを定義しましょう。
// moveメソッドを定義するMovableインターフェースを作成します
interface Movable { position: { x: number; y: number }; move: (x: number, y: number) => void;
} // 攻撃可能なオブジェクトを識別するためのDamageableインターフェースを作成します
interface Damageable { health: number; takeDamage: (damage: number) => void;
} // FighterインターフェースはDamageableを拡張し、attackの振る舞いを追加します
interface Fighter extends Damageable { attackPower: number; attack: (target: Damageable) => void;
}
5.2. Characterクラスの作成
次に、ゲーム内のすべてのキャラクターに共通する基本的なCharacter
クラスを作成しましょう。キャラクターは移動することができ、ダメージを受けることができます。
class Character implements Movable, Damageable { position = { x: 0, y: 0 }; health: number; constructor(x: number, y: number, health: number) { this.position.x = x; this.position.y = y; this.health = health; } move(x: number, y: number) { this.position.x = x; this.position.y = y; console.log(`(${this.position.x}, ${this.position.y})に移動しました`); } takeDamage(damage: number) { this.health -= damage; console.log(`${damage}のダメージを受けました。現在の体力: ${this.health}`); }
}
5.3. HeroクラスとEnemyクラスの作成
最後に、Hero
クラスとEnemy
クラスを作成しましょう。両方のクラスはCharacter
ですが、攻撃もできます。
class Hero extends Character implements Fighter { attackPower: number; constructor(x: number, y: number, health: number, attackPower: number) { super(x, y, health); this.attackPower = attackPower; } attack(target: Damageable) { console.log(`${this.attackPower}のダメージで敵を攻撃しました`); target.takeDamage(this.attackPower); }
} class Enemy extends Character implements Fighter { attackPower: number; constructor(x: number, y: number, health: number, attackPower: number) { super(x, y, health); this.attackPower = attackPower; } attack(target: Damageable) { console.log(`${this.attackPower}のダメージで主人公を攻撃しました`); target.takeDamage(this.attackPower); }
}
これらのクラスとインターフェースを使用して、主人公と敵との戦闘、キャラクターの移動などをシミュレーションすることができます。さらに、興味深く多様なゲームを作るために、さらに多くのアクションや属性を追加することもできます。
6. 結論と注意事項
私はクラスとインターフェースというオブジェクト指向プログラミングの重要な概念を紹介し、説明しました。ただし、クラスまたはインターフェースを使用するタイミング、および継承やポリモーフィズムを使用するかどうかの決定は、実際の問題によって異なります。これらの概念を柔軟に使用することで、ソースコードを理解しやすく管理しやすくすることができます。
7. よくある質問
-
クラスは常にコンストラクタが必要ですか? いいえ、コンストラクタは常に必要ではありません。ただし、オブジェクトが作成されたときに特定の値を初期化したい場合は、コンストラクタが適切な場所です。
-
クラスは複数のインターフェースを実装できますか? はい、クラスは複数のインターフェースを実装できます。これは、オブジェクトに複数の振る舞いを持たせたい場合に便利です。
-
継承を使用するタイミングはいつですか? 継承は、2つのクラスの間に「is-a」の関係がある場合に使用するべきです。例えば、「Hero」は「Character」である場合、継承を使用することができます。
-
スーパークラスのメソッドをオーバーライドできますか? はい、スーパークラスのメソッドをオーバーライドすることができます。これは、サブクラスでそのメソッドの動作を変更または拡張したい場合に便利です。
-
ポリモーフィズムはいつ使用するべきですか? ポリモーフィズムは、特定のアクションを実行したいが、実行するオブジェクトによって実行方法が異なる場合に使用するべきです。例えば、私たちの場合、各「Character」には「attack()」の振る舞いがありますが、具体的な実装は攻撃するオブジェクトによって異なります。
-
なぜインターフェースを使用する必要がありますか? インターフェースは、他のクラスが従う必要のある構造を定義するのに役立ち、ソースコードの一貫性を確保します。インターフェースを実装することで、特定のクラスが必要なメソッドを提供することを保証できます。
-
インターフェースからオブジェクトを作成できますか? いいえ、インターフェースから直接オブジェクトを作成することはできません。インターフェースは単にクラスが従うべき「インターフェース」を定義するものであり、具体的なメソッドを提供しません。
-
抽象クラスはいつ使用すべきですか? 抽象クラスは、一部のメソッドが事前に定義されている基本クラスを作成し、同時に一部のメソッドがサブクラスで定義される必要があることを保証したい場合に便利です。
Cuối cùng
Như thường lệ, mình hy vọng bạn thích bài viết này và biết thêm được điều gì đó mới.
Nếu bạn thích bài viết, xin hãy cho mình 1 upvote và đăng ký để ủng hộ mình có thêm động lực ra thêm nhiều bài viết hay hơn trong tương lại nhé.
Cảm ơn và hẹn gặp bạn trong những bài viết tiếp theo. Thank you. 😊
Ae nào muốn làm
Dev
hoặcBrSE
tại Nhật (N3-N4, 1-2 năm exp trở lên hoặc zero tech có tiếng N2-N3, cả 2 đầu Nhật và VN) cần mình đưa roadmap hoặc review CV, hoặc muốn hiểu hơn về các câu hỏi thường gặp khi interviewDev
hoặcBrSE
, cách deal lương cao... cần support thì cứ liên hệ mình qua zalo nhé: 0379302361 hoặc Facebook của mình. free