7
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

概要

ChatGPTはチャットボットとして、さまざまな分野でその活用の可能性が試されていると思います。

時にソフトウェア開発においても、驚くほどの可能性が見られます。

私も、色々試していますが、今回は一例として、すごく臭い?コードをRefactoringしてほしいと頼んでみます。コードの構造を改善しないと、今後の開発に困りそうなものに対して、どう直せばいいのか、ChatGPT先生の意見を聞き、できれば複数のソリューションをアドバイスしてもらいます。

※ 筆者の英語は上手くはありませんが、英語が色々望ましい結果を出しやすいので、英語で聞いています。
※ ChatGPTで回答される内容は、適切ではない回答の場合も多くあります。この記事では、基本プジティブな観点で主に書きます。実際には質問の仕方な内容が少し変わっても、色々回答が変わる場合や、意図とはかなり違う方向での回答、少し不適切なソリューションが出る場合もまだ多くありますので、ご注意ください。
※ この記事は個人の主観が多く含まれています。あくまで軽い参考レベルでみていただけると幸いです。もし間違ったところや他の意見がありましたら、コメントいただけると幸いです。

ChatGPT?

ChatGPTに関して20単語いないの紹介とリンクを教えてと聞いたら、「OpenAPIで開発された会話型言語モデル」と教えてくれますね。

image.png

Would you support Code Refactoring of my code to me?

image.png

ソフトウェアエンジニアリングにおいて、私はどういうところであなたのサポートを受けることが可能ですか?と聞いたら、以下のところを答えてくれましたね。

  • 重要概念、原則、ベストプラクティスの説明
  • 一般的なソフトウェア開発タスクと課題へのアプローチ方法に関するアドバイス
  • コードの品質、保守性、およびパフォーマンスを向上させるためのヒント
  • コードのデバッグとトラブルシューティングの支援
  • 特定のトピックに関するさらなる学習とリソースの推奨事項

今回は「コードの品質、保守性、およびパフォーマンスを向上させるためのヒント」にあたるところかもですね。

では、本格的にやっていきます。

ChatGPTにRefactoringの意見を聞いてみた

使うコードサンプルと質問

ふっと思いついたことをメモ帳で書いて、コンパイルのみ確認した、超適当コードです。
このコードはいろんな意味で、リファクタリングが必要そうですね。

//なんらかのゲームで、プレイヤーの攻撃力と武器の固有の計算方法で算出された、最終的な攻撃力を出す

//武器タイプ
enum WeaponType {
    GUN,
    SWORD,
}

//武器クラス
class Weapon {
    private WeaponType type;

    public Weapon(WeaponType type) {
        this.type = type;
    }

    public WeaponType getType() {
        return this.type;
    }
}

//プレイヤークラス
class Player {
    private int defaultAttackPoint;
    private Weapon equippedWeapon;

    public Player(Weapon weapon, int defaultAttackPoint) {
        this.equippedWeapon = weapon;
        this.defaultAttackPoint = defaultAttackPoint;
    }

    //★保有している武器の特性ごとに違う方法で攻撃ポイントを計算
    public int getAttackPoint() {
        switch (equippedWeapon.getType()) {
            case GUN: 
                return 50 + this.defaultAttackPoint;
            case SWORD:
                return (10 * this.defaultAttackPoint) * this.defaultAttackPoint;
            default:
                throw new RuntimeException("undefined weapon");
        }
    }
}

Q: how can i Refactoring this java code using design pattern?

image.png

「デザインパーンを仕入れて、私はどのようにリファクタリングができますか?」と聞いてみました。
image.png

A: One way to refactor this code using the Strategy design pattern is to extract the calculation of the attack point based on the weapon type into a separate class. This will allow you to easily add or modify the attack point calculation without changing the Player class.
Here's an example of how you can refactor the code:

A: 一つの方法として、戦略パターンを使ったリファクタリングで、武器固有の攻撃ポイント計算を、別途クラスで抽出することで、今後の追加な修正が容易になるということで、以下のように例も出してくれますね。

interface AttackPointCalculator {
    int calculateAttackPoint(int defaultAttackPoint);
}

class GunAttackPointCalculator implements AttackPointCalculator {
    public int calculateAttackPoint(int defaultAttackPoint) {
        return 50 + defaultAttackPoint;
    }
}

class SwordAttackPointCalculator implements AttackPointCalculator {
    public int calculateAttackPoint(int defaultAttackPoint) {
        return (10 * defaultAttackPoint) * defaultAttackPoint;
    }
}

class Player {
    private int defaultAttackPoint;
    private Weapon equippedWeapon;
    private AttackPointCalculator attackPointCalculator;

    public Player(Weapon weapon, int defaultAttackPoint) {
        this.equippedWeapon = weapon;
        this.defaultAttackPoint = defaultAttackPoint;

        switch (equippedWeapon.getType()) {
            case GUN: 
                this.attackPointCalculator = new GunAttackPointCalculator();
                break;
            case SWORD:
                this.attackPointCalculator = new SwordAttackPointCalculator();
                break;
            default:
                throw new RuntimeException("undefined weapon");
        }
    }

    public int getAttackPoint() {
        return attackPointCalculator.calculateAttackPoint(defaultAttackPoint);
    }
}

With this refactoring, you can easily add a new weapon type and corresponding attack point calculation by creating a new class that implements the AttackPointCalculator interface and modifying the Player class to use it.

攻撃ポイントを計算する要件のインタフェースを実装する新しいクラスを作って、プレイヤークラスで使うようにすると、新しい計算要求に対しても簡単に追加できるということですね。

※ 答えではここまでは書かれてないですが、戦略パターンを使うことで、IoC(制御の逆転)原則が自然に守られるので、最小限の制御・依存関係によって、より少ない影響範囲で機能追加・修正ができることがあるのではと思いますね。このロジックは、武器が追加されるたびに変わる可能性が大きいので、現段階でのコードにおいてはすごく適切なアドバイスかもしれません。

Q: what other ways i can consider?

image.png

ただし、戦略パターン以外で、見逃している観点はないか?という疑問が浮かびました。
「他の方法は検討できますか?」と、聞いてみました。
image.png

A: There are several other ways you can refactor this code to make it more flexible and maintainable. Here are a few options:

  • 1)Extract the weapon type and attack point calculation into a separate class: Instead of using an enumeration for the weapon type, you can define a class that represents a weapon and contains the attack point calculation logic. This would allow you to easily add or modify the attack point calculation without changing the Player class.
  • 2)Use the Factory design pattern: The Factory design pattern allows you to create objects without specifying their concrete classes. In this case, you can create a factory class that returns the appropriate attack point calculation object based on the weapon type.
  • 3)Use the Command design pattern: The Command design pattern allows you to encapsulate a request as an object, allowing you to separate the request from the object that performs the request. In this case, you can create a command object for each weapon type that contains the attack point calculation logic, and then use the command object to calculate the attack point.
  • 4)Use the Template Method design pattern: The Template Method design pattern defines the steps of an algorithm in a base class and allows subclasses to override certain steps. In this case, you can define a base class that contains the basic structure for calculating the attack point, and then create subclasses for each weapon type that override the specific steps needed to calculate the attack point.

Overall, the best solution will depend on the specific requirements and constraints of your application.

以下の他の方法が考えられるとアドバイスをもらいました。

  • 武器タイプと攻撃ポイント計算を別クラスで切り離す:enumを使う代わりにこうすることができて、プレイヤーのクラスは変更せずに、ポイント計算ロジックを追加できる
  • ファクトリーパターン:具体的なクラスに依存せず、武器のオブジェクトを生成できることで、武器のタイプ別に適切なポイント計算ロジックが含まれたオブジェクトを生成し返却されるファクトリークラスを作れる
  • コマンドパターン:ポイント計算の要求をコマンドとカプセル化し分離する。武器タイプごとのポイント計算ロジックを含むコマンドオブジェクトを作り利用できる
  • テンプレートメソッドパターン:計算アルゴリズム(※補足または抽象メソッド)を定義し、サブクラスが再定義できるようにする。この場合、攻撃ポイント計算の基本構造を含めた基本クラスをベースに、各自の武器タイプに対するサブクラスを作れる

「※ただし、最適のソリューションは、作るアプリケーションの詳細な要件によって変わる」

なるほど。考え方が広まるという意味で、なかなか勉強になりますね。

この中で、色々話をしてみたい内容はありますが、「テンプレートメソッドパターン」は、戦略パターンとやりたいことは似ているように見えるけど何か違うかな?と思ったりしましたね。

Q: Would you tell me the code Refactoring with Template Method design pattern?

image.png
「テンプレートメソッドパターンのリファクタリングしたコードをいただけますか?」と、質問してみました。

image.png

気前がいいですね!早速コード例を教えてくれます。

abstract class AttackPointCalculator {
    public int calculateAttackPoint(int defaultAttackPoint) {
        int attackPointModifier = getAttackPointModifier();
        return attackPointModifier * defaultAttackPoint;
    }

    abstract int getAttackPointModifier();
}

class GunAttackPointCalculator extends AttackPointCalculator {
    public int getAttackPointModifier() {
        return 50;
    }
}

class SwordAttackPointCalculator extends AttackPointCalculator {
    public int getAttackPointModifier() {
        return 10;
    }
}

class Player {
    private int defaultAttackPoint;
    private Weapon equippedWeapon;
    private AttackPointCalculator attackPointCalculator;

    public Player(Weapon weapon, int defaultAttackPoint) {
        this.equippedWeapon = weapon;
        this.defaultAttackPoint = defaultAttackPoint;

        switch (equippedWeapon.getType()) {
            case GUN: 
                this.attackPointCalculator = new GunAttackPointCalculator();
                break;
            case SWORD:
                this.attackPointCalculator = new SwordAttackPointCalculator();
                break;
            default:
                throw new RuntimeException("undefined weapon");
        }
    }

    public int getAttackPoint() {
        return attackPointCalculator.calculateAttackPoint(defaultAttackPoint);
    }
}

With this refactoring, the AttackPointCalculator class defines the basic structure for calculating the attack point, and the GunAttackPointCalculator and SwordAttackPointCalculator classes override the getAttackPointModifier method to provide the specific attack point calculation logic for each weapon type. This allows you to easily add or modify the attack point calculation for different weapon types without changing the Player class.

「これによって、AttackPointCalculatorクラスは、計算するための基本的な構造を定義し、武器タイプのクラスごとのgetAttackPointModifierメソッドを再定義して、武器を種類ごとの計算ロジックを提供する。これによって、プレイヤークラスは変更せずとも、新しい計算ロジックを加えることができる」

なるほどなるほど。

筆者は、戦略パターンだけ見た時は、戦略パターンでいいかな?と思いましたが、違うソリューションを聞いたら、色々迷いがでました。

戦略パターンとテンプレートメソッドパターンで、何を選ぶのが良いでしょう。。。

Q: What is key different when i choice one in Strategy pattern or Template Method Pattern?

image.png
「戦略パターンとテンプレートメソッドパターンのうち一つを選ぶとき、大事な違いはなんですか?」と、最後に聞いてみようと思います。

image.png

A: The Strategy design pattern and the Template Method design pattern are both behavioral design patterns that are used to define a set of steps or algorithms and allow them to be changed independently. However, there are some key differences between the two patterns:

二つのパターン両方同じく、一連の過程・アルゴリズムを定義し、独立した(※依存性が低い)感じで変更可能にできる「Behavioral Design Pattern」だと教えてくれました(やはり似たもの同士でしたね)

ただし、以下の大事な違いもあるということです。

  • 1)Responsibility: In the Strategy pattern, the responsibility for implementing the algorithm is delegated to a separate class (the strategy). In the Template Method pattern, the responsibility for implementing the algorithm is shared between the base class and the derived classes.
  • 2)Inheritance vs. Composition: The Strategy pattern uses composition, where an object is composed of other objects to achieve its behavior, whereas the Template Method pattern uses inheritance, where a subclass extends the behavior of a superclass.
  • 3)Flexibility: The Strategy pattern allows you to easily change the behavior of an object at runtime by changing the strategy it uses, whereas the Template Method pattern allows you to change the behavior of an object by subclassing it and overriding specific methods.
  1. 責任:「戦略パターン」は、過程・アルゴリズムの責任はinterfaceを継承する別クラス(戦略クラス)。「テンプレートメソッドパターンは」は、スーパークラスもサブクラスも持てるので共有される関係。
  2. 継承(Inheritance)とコンポジション: 「戦略パターン」は、オブジェクトが他のオブジェクトの実態と関係をもつComposition関係を使い関係構成。「テンプレートメソッドパターンは」は、サブクラスがスーパークラスの動作を拡張する継承を使い関係構成。
  3. 柔軟性:「戦略パターン」は、オブジェクトが使う戦略をランタイム時に簡単に変更できる(※Composition関係がもたらす利点ですかね)。反面「テンプレートメソッドパターン」は、サブクラスから特定メソッドをオーバーライドすることで、過程・アルゴリズムを変更できる(※オーバーライドしても、スーパークラスに色々従属してしまうケースは多いですね)

Overall, the best choice between the Strategy pattern and the Template Method pattern will depend on the specific requirements and constraints of your application.

「最終的には、どっちがいいのかは、アプリケーションの要件によって変わる」ということですね。

なるほどなるほど。色々考察をするところは多いですが、最初のあのしょぼいコードから、すごくいろんな観点での見方が出来ました。

GhatGPTの可能性?使ってみた感想

使ってみた感想は色々ありますが、今回リファクタリングにおいての感想はざっくりこんな感じです。

期待できるところ

  • 「いろんな観点での新しい着想になる素材」として、とっても興味ぶかいほど、驚くほどのアイディアをくれました。最初に答えに関しても、そこで終わらず、深く質問していくことで、やはり気づかなかったところや、固定観念として固まっていたところで、もっと柔軟な観点を得られることにすごく期待してます。
  • 「いろんな知識の習得とためのきっかけ」として、回答をそのまま丸ごと鵜呑みにすることはリスクがありますが、検証や調査するだけでも、かなりの知識を習得することはできると思います。今後も学習を通して、発展していくことを考えると、とっても楽しみです。

まだ難しいところ

  • 「推論能力が良すぎて、むしろ丸ごと鵜呑みにしてしまう可能性」が、難しいところだなと思いました。時に、正解がなく状況によって変わることで、各自で置かれた現実の要素を踏まえて決めるべきごとを、ChatGPTの回答をそのまま深く考えず取り入れてしまうのは、エンジニアリングの大事な本質の一つある思考能力をとめてしまうことに注意するべきかもしれません。
  • 「広範囲での推論に関しては、正確ではないか、エラーになる」ところが、まだ多いかなと感じました。今の段階では、簡単なリファクタリングやコードレビューは、非常にいい結果をくれますが、少しでも範囲が広まると、色々難しくなる感じがあります。しかし、ChatGPTはあくまでChatBotなので、近いうちにここまで可能なAIサービスが出るかなと思ったりします。
7
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?