TypeScriptは、JavaScriptに静的型付けを追加することで、より堅牢で保守性の高いコードを実現する言語です。その中でも「アクセス指定子」は、クラスのプロパティやメソッドに対するアクセス制限を行う重要な機能です。これにより、クラス外部からの不正な操作や予期せぬエラーを防ぎ、クラスのインターフェースを安全に設計することが可能です。本記事では、アクセス指定子の基本的な使い方から応用例までを詳しく解説し、堅牢なクラス設計のための知識を深めていきます。
アクセス指定子の基本
TypeScriptでは、クラス内のプロパティやメソッドに対してアクセス指定子を使い、アクセス範囲を制御することができます。アクセス指定子には主に以下の3つがあります。
public
public
は、クラス内外から自由にアクセスできることを示します。明示的に指定しない場合、すべてのプロパティやメソッドは自動的にpublic
となります。
private
private
は、クラス内部でのみアクセスできるメンバーを示します。外部から直接アクセスできず、データの隠蔽性を高めるために使用されます。
protected
protected
は、クラス内部およびそのクラスを継承したサブクラスからのみアクセス可能なメンバーを示します。サブクラスでの利用を許可しつつ、外部からのアクセスを制限する場合に使われます。
これらのアクセス指定子を活用することで、クラスのインターフェースをより厳密に制御し、コードの保守性や安全性を向上させることができます。
publicの役割
public
アクセス指定子は、クラスのプロパティやメソッドがどこからでもアクセスできることを示します。TypeScriptでは、デフォルトで指定されない限り、すべてのプロパティとメソッドはpublic
として扱われます。
publicを使う場面
クラス外部から頻繁にアクセスする必要があるメソッドやプロパティにはpublic
を使用します。これにより、外部コードから自由に参照や変更が可能となり、クラスの柔軟性を高めます。
例: クラス外からのアクセス
class User {
public name: string;
constructor(name: string) {
this.name = name;
}
public greet() {
console.log(`Hello, my name is ${this.name}`);
}
}
const user = new User("Alice");
user.greet(); // Hello, my name is Alice
user.name = "Bob"; // 名前を変更できる
この例では、name
プロパティとgreet
メソッドがpublic
として定義されているため、クラス外部から直接アクセスでき、必要に応じて変更や呼び出しが可能です。
publicのメリットとデメリット
public
は柔軟なアクセスを提供する一方で、外部から不適切に変更されるリスクも伴います。データの安全性が重要でない場合にのみ使用するのがベストです。
privateの役割
private
アクセス指定子は、クラス内部でのみアクセス可能なプロパティやメソッドを定義する際に使用します。これにより、クラス外部から直接操作されることを防ぎ、データの隠蔽性と安全性を確保します。
privateの用途
private
は、クラスの内部ロジックに関わる部分や、外部に公開したくないプロパティ・メソッドに適用されます。これにより、他の開発者が意図せずにデータを変更するリスクを回避できます。
例: クラス内部でのデータ保護
class BankAccount {
private balance: number;
constructor(initialBalance: number) {
this.balance = initialBalance;
}
public deposit(amount: number): void {
if (amount > 0) {
this.balance += amount;
}
}
public getBalance(): number {
return this.balance;
}
}
const account = new BankAccount(1000);
account.deposit(500); // 預金額を追加
console.log(account.getBalance()); // 1500
// account.balance = 2000; // エラー: 'balance' は private プロパティ
この例では、balance
プロパティはprivate
として定義されており、クラスの外部から直接アクセスすることができません。そのため、アカウントの残高はクラス内でのみ管理され、deposit
メソッドを通じて制御されます。
privateのメリットとデメリット
private
を使うことで、クラス外部からの不正な操作を防ぎ、内部のデータを保護できます。しかし、過剰に使用すると、クラスが閉鎖的になりすぎて柔軟性が失われることもあるため、適切なバランスが求められます。
protectedの使い道
protected
アクセス指定子は、クラス内部およびそのクラスを継承したサブクラス内でのみアクセス可能なプロパティやメソッドを定義するために使用します。private
と同様にクラス外部からのアクセスを制限しますが、サブクラスにはアクセス権を与えるため、クラスの継承と多態性をサポートしながらデータの保護が可能です。
protectedを使う場面
protected
は、継承されたクラスで使用するメソッドやプロパティを公開したいが、クラス外部からはアクセスを許可したくない場合に有効です。これにより、クラス階層内で共有されるが、外部のコードには露出しない設計が可能です。
例: 継承での利用
class Employee {
protected name: string;
constructor(name: string) {
this.name = name;
}
protected getDetails(): string {
return `Employee: ${this.name}`;
}
}
class Manager extends Employee {
private department: string;
constructor(name: string, department: string) {
super(name);
this.department = department;
}
public getManagerDetails(): string {
return `${this.getDetails()}, Department: ${this.department}`;
}
}
const manager = new Manager("Alice", "HR");
console.log(manager.getManagerDetails()); // Employee: Alice, Department: HR
// console.log(manager.name); // エラー: 'name' は protected プロパティ
この例では、name
プロパティとgetDetails
メソッドはprotected
として定義されているため、Employee
クラスのサブクラスであるManager
からはアクセスできますが、クラス外部から直接アクセスすることはできません。これにより、基本的なプロパティはサブクラスで引き継がれつつ、外部からは安全に保護されます。
protectedのメリットとデメリット
protected
は、クラスの継承を柔軟にサポートしつつ、データの隠蔽性を保ちます。サブクラスで利用したいが外部には公開したくない機能を提供できるため、コードの再利用性を高めることができます。ただし、過度に使用すると継承関係が複雑になり、クラス設計が難しくなる可能性があります。
クラスインターフェースの設計指針
TypeScriptにおけるアクセス指定子を効果的に活用することで、安全かつ効率的なクラスインターフェースの設計が可能です。アクセス指定子を正しく使うことで、クラスの内部構造を外部から隠蔽しつつ、必要な部分だけを公開することで、堅牢なオブジェクト指向設計を実現できます。
設計時の基本方針
アクセス指定子を使用したクラス設計の基本方針は、次の3つにまとめられます。
1. 必要最低限の公開
クラスのプロパティやメソッドをpublic
にするのは必要な部分だけに留め、可能な限りprivate
やprotected
を使用して外部からの不正な操作を防ぎます。クラスの内部構造を最小限に公開することが、メンテナンス性とセキュリティを高める重要なポイントです。
2. データの隠蔽
重要なデータや内部ロジックを外部から隠すためにprivate
を活用します。これにより、クラス外部から直接プロパティを操作するリスクを排除し、データの一貫性を保つことができます。
3. 継承の考慮
クラスを継承して使用する可能性がある場合、protected
を使用して、サブクラスでの再利用を容易にします。これにより、サブクラスが基本クラスの機能にアクセスしつつ、クラス外部には露出しない設計が可能になります。
クラス設計の例
次の例では、アクセス指定子を使って設計されたクラスがどのように内部のデータを保護しつつ、必要な部分のみを公開しているかを示しています。
class User {
private password: string;
protected role: string;
public name: string;
constructor(name: string, password: string, role: string) {
this.name = name;
this.password = password;
this.role = role;
}
public updatePassword(newPassword: string): void {
if (newPassword.length >= 8) {
this.password = newPassword;
} else {
console.log("パスワードは8文字以上にしてください");
}
}
protected getRole(): string {
return this.role;
}
}
この例では、password
はprivate
で隠されており、クラス外部からは直接操作できません。一方、role
はprotected
として定義され、サブクラス内で利用できるようにしつつ外部には公開されません。name
はpublic
で定義され、外部から自由にアクセス可能です。
最適なアクセス指定子の選択
クラスインターフェースを設計する際は、各メンバーのアクセス範囲を適切に選定することで、コードの安全性と可読性が向上します。データの隠蔽とクラスの再利用性のバランスを保ちながら、アクセス指定子を活用して堅牢なクラスを設計することが、優れたTypeScriptコードの基礎となります。
アクセス指定子を使ったエラーの防止
アクセス指定子は、クラスのプロパティやメソッドのアクセス範囲を制限することで、意図しないデータの変更や誤った操作を防ぐ重要な役割を果たします。特に、private
やprotected
を適切に活用することで、クラス外部からの不正な操作を抑止し、予期しないエラーを減少させることができます。
データの安全性を確保する
private
やprotected
アクセス指定子を使うことで、クラスの内部データを外部から直接操作できないように制御できます。これにより、予期しないデータの変更や破壊を防ぎ、データの一貫性と安全性を保つことが可能です。
例: 誤操作を防ぐ
以下の例では、private
を使って、クラス外部からの不正なデータ操作を防いでいます。
class BankAccount {
private balance: number;
constructor(initialBalance: number) {
this.balance = initialBalance;
}
public deposit(amount: number): void {
if (amount > 0) {
this.balance += amount;
} else {
console.log("預金額は正の数である必要があります");
}
}
public getBalance(): number {
return this.balance;
}
}
const account = new BankAccount(1000);
account.deposit(500); // 残高が1500に増加
account.deposit(-100); // エラー: 負の金額は許可されない
console.log(account.getBalance()); // 1500
// account.balance = 2000; // エラー: 'balance'は private プロパティ
この例では、balance
プロパティがprivate
で保護されているため、外部から直接操作することができません。これにより、クラス内で定義されたdeposit
メソッドを通じてのみ残高の変更が許可され、誤った操作が排除されます。
カプセル化によるトラブル防止
カプセル化は、クラスのデータを外部から隠蔽し、アクセスを制御することで、予期しないエラーやバグを防ぎます。アクセス指定子を使うことで、クラスの内部状態に対して一貫した操作が保証され、予測可能な動作を提供します。
例: 許可されていないアクセスの防止
class Employee {
private salary: number;
constructor(salary: number) {
this.salary = salary;
}
public getSalary(): number {
return this.salary;
}
public setSalary(newSalary: number): void {
if (newSalary > 0) {
this.salary = newSalary;
} else {
console.log("給与は正の数でなければなりません");
}
}
}
const employee = new Employee(50000);
employee.setSalary(-1000); // エラー: 負の給与は設定できない
console.log(employee.getSalary()); // 50000
ここでも、salary
プロパティがprivate
で保護されているため、外部からの不正な操作が防がれています。setSalary
メソッドを使って、適切な範囲の値のみを受け入れることで、予期せぬエラーを防止しています。
エラー防止の効果
アクセス指定子を使用することで、コードベース全体の予測可能性が向上し、誤操作によるバグやセキュリティ上の問題を防ぎやすくなります。また、他の開発者がクラスを利用する際、どのプロパティやメソッドにアクセスできるかが明確になるため、意図しないコード変更を減少させる効果もあります。
アクセス指定子を効果的に活用することで、エラーを防ぎ、堅牢で信頼性の高いプログラムを設計できるようになります。
継承を伴うクラス設計の実例
TypeScriptのアクセス指定子を使用した継承を伴うクラス設計は、オブジェクト指向プログラミングにおいて強力なツールとなります。アクセス指定子private
やprotected
を使用することで、クラス間での適切なデータの共有と隠蔽が可能になります。ここでは、継承に基づいたクラス設計の具体例を見ていきます。
継承におけるprotectedの活用
protected
アクセス指定子は、親クラスで定義されたプロパティやメソッドをサブクラスで利用できるようにしつつ、外部からのアクセスを制限するために使われます。これにより、継承関係にあるクラス間での柔軟なアクセス管理が可能です。
例: 基本クラスとサブクラス
class Person {
protected name: string;
protected age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
protected getDetails(): string {
return `Name: ${this.name}, Age: ${this.age}`;
}
}
class Employee extends Person {
private employeeId: string;
constructor(name: string, age: number, employeeId: string) {
super(name, age); // 親クラスのコンストラクタを呼び出し
this.employeeId = employeeId;
}
public getEmployeeInfo(): string {
return `${this.getDetails()}, Employee ID: ${this.employeeId}`;
}
}
const employee = new Employee("John Doe", 30, "E12345");
console.log(employee.getEmployeeInfo()); // Name: John Doe, Age: 30, Employee ID: E12345
// console.log(employee.name); // エラー: 'name'はprotectedプロパティであり外部からはアクセス不可
この例では、Person
クラスのname
とage
プロパティ、getDetails
メソッドはprotected
として定義されているため、サブクラスのEmployee
からアクセス可能ですが、クラス外部からは直接アクセスできません。このように、protected
を使用することで、必要な部分だけをサブクラスに引き継ぎ、クラス外部への不要な露出を防ぎます。
継承とカプセル化のバランス
継承を利用したクラス設計では、データのカプセル化(隠蔽)とサブクラスによる再利用のバランスが重要です。protected
を使用することで、クラスの内部状態を適度に隠蔽しながら、サブクラスに必要な機能を提供できます。一方、private
を使うことで、サブクラスにも公開しない完全なカプセル化が可能です。
例: privateとprotectedの使い分け
class Vehicle {
protected speed: number;
private fuel: number;
constructor(speed: number, fuel: number) {
this.speed = speed;
this.fuel = fuel;
}
protected getSpeed(): number {
return this.speed;
}
private refuel(amount: number): void {
this.fuel += amount;
}
public drive(): void {
if (this.fuel > 0) {
console.log(`Driving at ${this.speed} km/h`);
this.fuel -= 1;
} else {
console.log("Out of fuel");
}
}
}
class Car extends Vehicle {
private model: string;
constructor(model: string, speed: number, fuel: number) {
super(speed, fuel);
this.model = model;
}
public showInfo(): void {
console.log(`Model: ${this.model}, Speed: ${this.getSpeed()} km/h`);
}
}
const car = new Car("Sedan", 120, 10);
car.showInfo(); // Model: Sedan, Speed: 120 km/h
car.drive(); // Driving at 120 km/h
// car.refuel(10); // エラー: 'refuel' は private でありクラス外部からはアクセス不可
この例では、Vehicle
クラスのspeed
プロパティとgetSpeed
メソッドはprotected
として定義され、Car
クラスで再利用されています。一方、fuel
プロパティとrefuel
メソッドはprivate
として隠蔽されており、Car
クラスや外部からは直接アクセスできません。これにより、クラスの内部状態を適切に保護しつつ、必要な機能だけをサブクラスで活用できる設計が実現されています。
クラス設計のベストプラクティス
継承を伴うクラス設計では、アクセス指定子を適切に使い分けることが重要です。private
を使ってクラスの内部状態を厳密に隠蔽し、protected
を使ってサブクラスに必要なデータやメソッドを提供することで、クラスの安全性と再利用性を高めることができます。また、public
を最小限に使用し、外部からのアクセスを制御することで、堅牢でメンテナンスしやすいコードを構築することができます。
アクセス指定子とインターフェースの違い
TypeScriptでは、クラスのアクセス指定子(public
、private
、protected
)とインターフェース(interface
)は、クラスの設計において異なる役割を果たします。どちらもコードの安全性と可読性を向上させますが、使い方や目的が異なるため、適切に理解し使い分けることが重要です。
アクセス指定子の役割
アクセス指定子は、クラスのプロパティやメソッドのアクセス範囲を制御します。アクセス指定子を使うことで、クラスの内部データを外部から隠蔽したり、サブクラスに限定したアクセスを提供したりします。つまり、クラスの外部と内部でのデータのやり取りを安全に管理するための手段です。
例: アクセス指定子を使用したクラス
class User {
public name: string;
private password: string;
constructor(name: string, password: string) {
this.name = name;
this.password = password;
}
public getName(): string {
return this.name;
}
private getPassword(): string {
return this.password;
}
}
この例では、name
はpublic
で定義されており、クラス外部からアクセス可能ですが、password
はprivate
として保護されており、クラス外部からはアクセスできません。アクセス指定子は、クラスの設計においてデータの保護を実現するためのツールです。
インターフェースの役割
一方、interface
は、クラスがどのようなメソッドやプロパティを持つべきかを定義するための型情報を提供します。インターフェースはクラスの「契約」を定義し、そのクラスがどのような機能を公開するかを明確にします。インターフェース自体には実装は含まれず、実際のロジックやアクセス制御はクラス側で行います。
例: インターフェースの定義と使用
interface UserInterface {
name: string;
getName(): string;
}
class User implements UserInterface {
public name: string;
private password: string;
constructor(name: string, password: string) {
this.name = name;
this.password = password;
}
public getName(): string {
return this.name;
}
}
この例では、UserInterface
インターフェースは、name
プロパティとgetName
メソッドを持つことを要求しています。User
クラスはこのインターフェースを実装することで、インターフェースで定義された契約を遵守します。インターフェースは、クラスがどのような機能を外部に提供するかを定義するために使われます。
アクセス指定子とインターフェースの違い
- アクセス指定子: クラス内でのプロパティやメソッドの可視性を制御します。内部データを保護し、不正な操作を防ぐために使用されます。
- インターフェース: クラスの外部に公開するメソッドやプロパティの仕様を定義します。複数のクラスに共通の契約を持たせ、型安全性を高めるために使用されます。
アクセス指定子とインターフェースの補完関係
アクセス指定子とインターフェースは、それぞれ異なる目的を持っていますが、相互に補完し合う形で使用できます。インターフェースを使ってクラスの外部インターフェースを定義し、アクセス指定子で内部データの保護やアクセス範囲を制御することで、柔軟かつ安全な設計が可能です。
どちらを使うべきか
- クラスの内部データの保護が必要な場合や、サブクラスに限定されたアクセスを提供したい場合は、アクセス指定子を使用します。
- クラスの外部との一貫したインターフェースを提供し、複数のクラスが共通のメソッドやプロパティを持つ必要がある場合は、インターフェースを使用します。
両方を適切に組み合わせることで、強力なオブジェクト指向設計を実現できます。
実装例: アクセス指定子を用いたセキュアなクラス
TypeScriptにおけるアクセス指定子を利用することで、クラスのセキュリティを高め、データの安全な管理が可能になります。ここでは、public
、private
、およびprotected
を使用して、実際にアクセス制限を設けたクラスの実装例を紹介します。
例: ユーザー認証クラス
以下は、ユーザーの名前とパスワードを管理し、認証メソッドを備えたクラスの実装例です。このクラスは、private
を使ってパスワードを隠蔽し、外部から直接アクセスできないように設計されています。
class User {
public name: string;
private password: string;
constructor(name: string, password: string) {
this.name = name;
this.password = password;
}
// パスワードを更新するためのメソッド
public updatePassword(oldPassword: string, newPassword: string): string {
if (this.verifyPassword(oldPassword)) {
this.password = newPassword;
return "パスワードが更新されました。";
} else {
return "現在のパスワードが間違っています。";
}
}
// 認証処理を行うメソッド
public authenticate(password: string): boolean {
return this.verifyPassword(password);
}
// プライベートメソッドでパスワードの確認を行う
private verifyPassword(password: string): boolean {
return this.password === password;
}
}
// クラスのインスタンス化
const user = new User("John Doe", "securePassword123");
// 認証の実行
console.log(user.authenticate("securePassword123")); // true
// パスワードの更新
console.log(user.updatePassword("securePassword123", "newPassword456")); // パスワードが更新されました。
console.log(user.authenticate("newPassword456")); // true
// パスワードには直接アクセスできない
// console.log(user.password); // エラー: 'password' は private プロパティ
実装のポイント
1. publicプロパティとメソッド
public
で定義されたname
プロパティやauthenticate
メソッドは、クラス外部から自由にアクセスできます。これにより、ユーザー名の表示や、認証メソッドの実行を外部から可能にしています。
2. privateプロパティとメソッド
password
プロパティはprivate
として定義されており、外部からは直接アクセスできません。このため、ユーザーのパスワードが外部に漏れたり、他の部分で意図せず変更されたりするリスクを防いでいます。また、verifyPassword
メソッドもprivate
として定義され、クラス内でしか使用できない隠蔽されたロジックとなっています。
3. 安全なメソッドの公開
updatePassword
メソッドは、パスワード変更のために公開されているpublic
メソッドですが、内部でprivate
メソッドのverifyPassword
を使用して、現在のパスワードが正しいかどうかを確認します。これにより、安全性を確保しつつ、必要な部分だけを外部に公開しています。
保守性と安全性のバランス
この実装例では、アクセス指定子を使うことで、クラスの内部データ(パスワード)を適切に保護しつつ、必要な機能(認証やパスワード変更)だけを外部に提供しています。これにより、クラスの利用者が誤って内部データを操作することを防ぎ、コードの安全性を高めることができます。
アクセス指定子を効果的に利用することで、外部からの不正な操作や予期せぬエラーを防ぎ、保守性の高いクラス設計が実現できることが、この例からもわかります。
応用編: 複雑なクラス構成でのアクセス指定子の活用
アクセス指定子は、単一のクラス内だけでなく、複雑なクラス構成においても強力に機能します。複数のクラスが連携する大規模なシステムでは、データの隠蔽と共有を適切に管理することで、コードの保守性や再利用性が大幅に向上します。ここでは、複雑なクラス構成においてアクセス指定子を応用する具体的な例を紹介します。
例: 継承と多態性を含むクラス構成
以下の例では、private
、protected
、およびpublic
を適切に使い分けた複雑なクラス設計を行い、クラス間でのデータ共有や制御を行っています。
class Animal {
protected name: string;
constructor(name: string) {
this.name = name;
}
protected makeSound(): string {
return `${this.name} is making a sound.`;
}
}
class Dog extends Animal {
private breed: string;
constructor(name: string, breed: string) {
super(name);
this.breed = breed;
}
public bark(): string {
return `${this.name}, the ${this.breed}, is barking!`;
}
public describe(): string {
return `This is a ${this.breed} named ${this.name}.`;
}
protected fetch(): string {
return `${this.name} is fetching the ball.`;
}
}
class GuideDog extends Dog {
private certificationNumber: string;
constructor(name: string, breed: string, certificationNumber: string) {
super(name, breed);
this.certificationNumber = certificationNumber;
}
public assist(): string {
return `${this.name} is assisting with certification number ${this.certificationNumber}.`;
}
public guide(): string {
return `${this.name} is guiding the person.`;
}
public performTasks(): string {
return `${this.bark()} Also, ${this.fetch()}`;
}
}
const rex = new GuideDog("Rex", "Labrador", "G12345");
console.log(rex.assist()); // Rex is assisting with certification number G12345.
console.log(rex.bark()); // Rex, the Labrador, is barking!
console.log(rex.performTasks()); // Rex, the Labrador, is barking! Also, Rex is fetching the ball.
実装のポイント
1. protectedを使った継承関係でのデータ共有
Animal
クラスのname
プロパティはprotected
として定義されています。そのため、Dog
クラスやGuideDog
クラスではname
プロパティにアクセス可能ですが、クラスの外部からはアクセスできません。これにより、基本的な情報はサブクラスで利用しつつ、外部には隠蔽されています。
2. privateプロパティのセキュリティ
Dog
クラスのbreed
プロパティや、GuideDog
クラスのcertificationNumber
プロパティはprivate
として定義されています。これにより、これらのデータはクラス外部からは直接アクセスできず、誤った操作や情報の漏洩を防ぐことができます。
3. publicメソッドによる情報の公開
bark
やassist
のようなpublic
メソッドは、外部に対してクラスの動作を提供するためのインターフェースとして機能しています。これにより、ユーザーはクラスの内部構造を知らなくても、必要な操作を実行できます。
多態性の活用
この設計では、多態性(ポリモーフィズム)を活用し、GuideDog
クラスがDog
クラスの機能を拡張しています。GuideDog
はDog
のbark
やfetch
メソッドを継承しつつ、新たにassist
やguide
といった独自の機能を追加しています。これにより、より複雑で多機能なクラス構成が実現できます。
クラス間でのアクセス制御のバランス
アクセス指定子を適切に使い分けることで、クラス間でのデータ共有を必要最小限に抑えつつ、機能拡張や再利用を容易にします。例えば、protected
を使うことで、サブクラスに必要なデータは継承しながら、外部からのアクセスを防ぐ設計が可能です。また、private
を用いることで、クラスの内部状態を厳密に保護し、不正な操作やエラーを防ぐことができます。
複雑なクラス構成での設計のポイント
protected
を使用して、クラス間で共有する必要のあるプロパティやメソッドにアクセスできるようにする。private
で外部から直接アクセスされないようにデータを保護し、必要な場合はpublic
メソッドを通じて操作を提供する。- サブクラスでは親クラスの機能を拡張し、再利用性を高めつつ、必要なデータや機能のみを継承する。
このようにアクセス指定子を効果的に活用することで、複雑なクラス構成でも安全かつ柔軟な設計が可能になります。
まとめ
本記事では、TypeScriptのアクセス指定子(public
、private
、protected
)を使ってクラスのインターフェースを安全に設計する方法について解説しました。各指定子の役割や使い方、さらに継承や多態性を取り入れたクラス設計の応用例を通じて、アクセス指定子を使うことでデータの安全性やコードの保守性を高めることができることがわかりました。アクセス指定子を適切に活用し、クラス設計の品質を向上させましょう。
コメント