MofuMofu2 / typescript-sandbox

MIT License
0 stars 0 forks source link

feature: #49 クラスを追加 #50

Closed MofuMofu2 closed 1 year ago

MofuMofu2 commented 1 year ago

constructorが初期化時に呼ばれる処理というのはわかった。プロパティの持ち方がよくわからん

resolve #49

MofuMofu2 commented 1 year ago

採点結果


提出いただいたコードを確認しました。以下に詳細を記載します。

  1. 型の定義:

    • AnimalModel を適切に定義しています。動物の名前と種類をプロパティとして持つ要件を満たしています。
  2. クラスの定義:

    • Animal クラスを定義しており、コンストラクタに AnimalModel 型のパラメータを取るようになっています。また、コンストラクタ内で動物の情報をコンソールに出力しています。
  3. インスタンスの作成:

    • catdog の2つのインスタンスを作成し、正しく動作しています。

採点結果: 90点

提出されたコードは基本的に要件を満たしていますが、一点提案があります。Animal クラス内でプロパティとして namekind を持たせると、このクラスがより実用的になります。そして、情報出力のようなメソッドも追加することが可能になります。

例:

class Animal {
  name: string;
  kind: string;

  constructor(animal: AnimalModel) {
    this.name = animal.name;
    this.kind = animal.kind;
  }

  displayInfo(): void {
    console.log(`${this.name} is ${this.kind}.`);
  }
}

const cat = new Animal({ name: "Cat", kind: "mammal" });
cat.displayInfo();

const dog = new Animal({ name: "Dog", kind: "mammal" });
dog.displayInfo();

このように、クラス内でのプロパティ定義とメソッドの追加を検討してみると良いでしょう。

MofuMofu2 commented 1 year ago

クラスについて質問してみた。


もちろんです。クラスのプロパティについて詳しく解説いたします。

クラスのプロパティとは

クラスのプロパティ(またはメンバ変数、フィールドとも呼ばれる)は、クラスの中で定義された変数のことを指します。プロパティは、クラスのインスタンス(オブジェクト)の状態やデータを保持するために使用されます。

プロパティの利点

  1. 状態の保持: クラスのインスタンスごとに独自の状態を持つことができます。例えば、前回のAnimalクラスでは、各動物インスタンスが自身のnamekindを持つことができます。

  2. カプセル化: クラス内でプライベートなプロパティを定義することで、外部からのアクセスを制限することができます。これにより、不正な値の設定や意図しない変更を防ぐことができます。

  3. 再利用性: クラスのメソッド内でプロパティを使用することで、同じ処理を再利用しやすくなります。

TypeScriptでのプロパティの定義

TypeScriptでは、プロパティの定義は以下のように行います。

class MyClass {
  propertyName: type;
}

例:

class Animal {
  name: string;
  kind: string;
}

TypeScriptでのプロパティの初期化

TypeScriptでは、コンストラクタ内でプロパティを初期化することが一般的です。

class Animal {
  name: string;
  kind: string;

  constructor(name: string, kind: string) {
    this.name = name;
    this.kind = kind;
  }
}

また、TypeScriptの特性として、ショートハンドプロパティ初期化を使用することで、コンストラクタ引数とプロパティ定義を同時に行うことができます。

class Animal {
  constructor(
    public name: string,
    public kind: string
  ) {}
}

この方法で、コンストラクタの引数として受け取ったnamekindは、自動的にクラスのプロパティとして初期化されます。

これらの概念を取り入れて、クラスを設計することで、より堅牢で再利用性の高いコードを書くことができます。

MofuMofu2 commented 1 year ago

ということらしい。提案ではthis.name = animal.name のようにしているが、どうしてだろうか。

export class ThrowError extends Error {
  responseData: MessageModel; ☜これがプロパティ
  constructor(message: string, responseData: MessageModel) {
    super(message);
    this.responseData = responseData;
  }
}
MofuMofu2 commented 1 year ago

https://github.com/MofuMofu2/typescript-sandbox/pull/50/commits/d5a664a1d79d9327636f41504be50c6f4ab517f8 のコメント

現在のコードでは、Animal クラスが animal という名前の AnimalModel 型のプロパティを持っており、その中に namekind が格納されています。これにより、動物の情報へのアクセスが少し複雑になります。例えば、特定の動物の名前にアクセスしたい場合、以下のように2段階の参照が必要です。

const someAnimal = new Animal({ name: "Lion", kind: "mammal" });
console.log(someAnimal.animal.name);  // "Lion"

これを、namekindAnimal クラスの直接のプロパティとして持たせるように変更すると、次のような設計になります。

class Animal {
  name: string;
  kind: string;

  constructor(name: string, kind: string) {
    this.name = name;
    this.kind = kind;
  }
}

この設計の場合、新しい動物を作成する際のコードは以下のようになります。

const someAnimal = new Animal("Lion", "mammal");

そして、特定の動物の名前や種類にアクセスするためのコードは次のようにシンプルになります。

console.log(someAnimal.name);  // "Lion"
console.log(someAnimal.kind);  // "mammal"

このような設計のメリットは以下の通りです:

  1. シンプルさ: クラスの利用者が namekind に直接アクセスできるため、コードが読みやすく、直感的になります。
  2. 再利用性: Animal クラスのメソッドを作成する際、this.namethis.kind といったシンプルな形でプロパティにアクセスできるため、メソッドの作成が容易になります。
  3. 拡張性: 将来的に動物の情報に新しいプロパティ(例:colorweight など)を追加する場合、直接クラスのプロパティとして追加すれば良いので、拡張が容易になります。

これらの理由から、namekindAnimal クラスの直接のプロパティとして持たせる方が、多くのケースで好まれる設計と言えます。

MofuMofu2 commented 1 year ago

何回もコードを再利用するので、代入時の考慮よりはアクセスのしやすさを重視するのかもしれないなと思った。 この辺が全然思想が異なる点なのかなと思った。コンポーネント思考は状態を返すので関数寄りの思考で、オブジェクト思考はあまりしないのかもしれない。


はい、その通りです。クラスと関数は、それぞれ異なる設計の思考やパターンを持ちます。以下に、その違いの要点をまとめてみました。

関数

  1. 無状態: 一般的に関数は無状態で、ある入力に対して常に同じ出力を返すことが期待されます(純粋関数)。
  2. 再利用性: 関数は独立して動作するため、再利用性が高いです。
  3. 単純さ: 関数は特定の操作をカプセル化するため、簡潔で読みやすいコードを書くことができます。
  4. 関数型プログラミング: 関数は関数型プログラミングの基本的な概念として重要な役割を果たします。高階関数やクロージャなどのパターンは関数型プログラミングに特有のものです。

クラス

  1. 状態管理: クラスは内部の状態を持ち、その状態に基づいて動作することが多いです。例えば、上述のAnimalクラスの例では、namekindという状態を持っています。
  2. カプセル化: クラスはデータとそのデータに関連する動作(メソッド)を一緒にカプセル化することができます。
  3. 継承: クラスは継承を利用して、既存のクラスを拡張したり、新しいクラスを作成したりすることができます。
  4. ポリモーフィズム: クラスを利用すると、異なるクラスで同じインターフェースやメソッドを持つことができ、ポリモーフィズムを実現することができます。

これらの違いを理解しておくことで、適切な時と場所で関数やクラスを選択することができ、より効果的なプログラミングが可能になります。関数とクラス、それぞれの設計のアプローチや考え方にはそれぞれの利点と適用場面がありますので、状況に応じて適切に選択することが重要です。

MofuMofu2 commented 1 year ago

https://github.com/MofuMofu2/typescript-sandbox/pull/50/commits/ac5f835ce9978a1a4d6b4d9351d7863ce5fac8e4 の採点


修正されたコードを確認しました。良い進展です!

以下に、コードの評価を行います。

ポジティブな点

  1. 直接的なプロパティ: namekindAnimal クラスの直接のプロパティとして設定されています。これにより、クラスの利用者が簡単にこれらのプロパティにアクセスできます。

  2. シンプルさ: コードはシンプルで直感的です。動物の情報へのアクセスも容易になっています。

さらなる改善点

  1. 型注釈: TypeScriptを使用している場合、クラスのプロパティにも型注釈をつけることで、より強固な型安全性を持たせることができます。以下のように、クラスのプロパティnamekindに型注釈をつけると良いでしょう。

    class Animal {
     name: string;
     kind: string;
     // ...
    }
  2. コンストラクタの引数: 現在はコンストラクタの引数としてオブジェクト(AnimalModel)を受け取っていますが、直接 namekind を受け取るようにすると、より直感的になります。以下のように変更すると良いでしょう。

    class Animal {
     constructor(name: string, kind: string) {
       this.name = name;
       this.kind = kind;
     }
    }

    そして、インスタンスを作成する際には次のようにします。

    const cat = new Animal("Cat", "mammal");

全体として、良い方向に進んでいます。上記の提案を取り入れることで、さらに良いコードになるでしょう。


1の型注釈は行なっている気がする。多分コロンがないのでパースできなかったのかもしれない。 この辺は推論できないかも。

MofuMofu2 commented 1 year ago

2はオブジェクトではダメな理由が気になったので質問してみた。


オブジェクトを受け取らず別々に受け取ると直感的になるという点について解説いただきたいです。4・5個受け取る必要が出てきたら流石にオブジェクトの方が良いのではないかと思いました。

ご指摘の通り、引数の数が多くなってくると、関数やコンストラクタの引数として直接受け取る方法はコードの読みやすさやメンテナンス性に悪影響を与える可能性があります。特に、引数の順番を覚えるのが困難になる、または引数の順番が変更されたときのリファクタリングが大変になるという問題が考えられます。

オブジェクトを受け取る方法の利点:

  1. 引数の順番が柔軟: 引数の順番を気にすることなく、オブジェクトを渡せます。
  2. 拡張性: 新しいプロパティや引数を追加する際に、既存のコードに影響を与えずに拡張できます。
  3. 明確性: 引数が多くなった場合、オブジェクトのキーを使ってどの値が何を意味するのかを明確に示すことができます。

直接受け取る方法の利点:

  1. シンプルさ: 小さな数の引数であれば、コンストラクタや関数の定義と使用がシンプルになります。
  2. 型チェック: TypeScriptを使用している場合、各引数に型注釈を付けることで、より明確な型チェックが行えます。

総じて、どちらの方法を選ぶかは、ケースバイケースです。少数の引数の場合は、直接受け取る方法がシンプルで直感的かもしれません。しかし、引数が4つ、5つ以上になる場合、または将来的に追加される可能性がある場合は、オブジェクトを受け取る方法が推奨されます。

ご指摘の通り、オブジェクトを受け取る方が柔軟で読みやすい場合も多いです。コンテキストや要件に応じて最適な方法を選択することが重要です。


2つともstring型だったら順番が逆でも気づけなさそう。そういう意味でも複数あって型が同じならオブジェクトで受け取る方が安全そうな気がした。2つ程度だったらバラバラにするお作法を感じたら合わせておくのが良さそう。

MofuMofu2 commented 1 year ago

すごくChatGPTを活用できた気がする。