魁!dcWORKS塾 〜エンジニア編 vol.6〜

こんにちは! dcWORKSでエンジニアをしているミネです。
弊社のエンジニアチームやデザインチームの実態をより詳しく知ってもらうべく始まった「魁!dcWORKS塾」エンジニア編 第6回をお届けしていきます!

魁!dcWORKS塾とは?

技術研修や勉強会など、それぞれで実際に取り組んでいるような内容を問題・課題の形式でご紹介します。
エンジニアの問題では解答や解決するためのポイントを、またデザインチームでは制作過程など、各チームの取り組みを具体的に紹介していきますのでお楽しみに!
BLOGを見てくださっているみなさんも一緒に楽しみながら取り組んでいただけると嬉しいです。

エンジニア編 第6回ということで、弊社が運営しているプログラミングスクールのEngineersGateでも講師を務めるサトウ先生からの出題です。

人物紹介・サトウ先生
エンジニアズゲート講師。みんなから愛されるキャラクターとボディーの持ち主

問題

加減乗除の計算を、オブジェクト指向に沿って実装してください。
また、その説明を該当部分にコメントで記載してください

オブジェクト指向の主要な特徴
  1. クラスとオブジェクト
    クラスはオブジェクトの設計図であり、オブジェクトはクラスのインスタンスです。
    クラスは属性(データメンバー、またはプロパティ)とメソッド(関数)を持ち、オブジェクトはそれらの属性とメソッドを実際に持つインスタンスです。
  2. カプセル化
    データと操作をクラス内でカプセル化し、クラスの外部からはアクセス制御を施すことができます。
    これにより、データの隠蔽とセキュリティが向上し、クラスの内部実装を変更せずに外部インターフェースを変更できます。
  3. 継承
    既存のクラスを拡張し、新しいクラスを作成できます。
    継承により、既存のクラスの属性とメソッドを再利用できます。
    親クラス(スーパークラス)と子クラス(サブクラス)の関係があります。
  4. ポリモーフィズム
    同じ名前のメソッドが異なるクラスで異なる振る舞いをすることができます。
    これにより、異なるクラスのオブジェクトを一貫して操作できます。
  5. 抽象化
    実世界のエンティティを抽象化してモデル化する能力。
    重要な属性や操作に焦点を当て、不要な詳細を省略します。

みなさん、解けましたか?
解答は下記です!サトウ先生からのワンポイントアドバイスもあるのでぜひ参考にしてみてください!

解答例

Main.java

package lesson_05;

/**
* 加減乗除の計算を、オブジェクト指向にそって実装してください。
* また、その説明を該当部分にコメントで記載してください。
*
* オブジェクト指向の主要な特徴:
* 1.クラスとオブジェクト:
* クラスはオブジェクトの設計図であり、オブジェクトはクラスのインスタンスです。
* クラスは属性(データメンバー、またはプロパティ)とメソッド(関数)を持ち
* オブジェクトはそれらの属性とメソッドを実際に持つインスタンスです。
* 2.カプセル化:
* データと操作をクラス内でカプセル化し、クラスの外部からはアクセス制御を施すことができます。
* これにより、データの隠蔽とセキュリティが向上し
* クラスの内部実装を変更せずに外部インターフェースを変更できます。
* 3.継承:
* 既存のクラスを拡張し、新しいクラスを作成できます。
* 継承により、既存のクラスの属性とメソッドを再利用できます。
* 親クラス(スーパークラス)と子クラス(サブクラス)の関係があります。
* 4.ポリモーフィズム:
* 同じ名前のメソッドが異なるクラスで異なる振る舞いをすることができます。
* これにより、異なるクラスのオブジェクトを一貫して操作できます。
* 5.抽象化:
* 実世界のエンティティを抽象化してモデル化する能力。
* 重要な属性や操作に焦点を当て、不要な詳細を省略します。
*
* ※「1.クラスとオブジェクト」については、定義した各クラスの構造についての概要になります。
* 各計算クラスを参照し、「クラスは属性(データメンバー、またはプロパティ)とメソッド(関数)を持つ」ことを
* 確認ください。
* また、実際に定義したクラスを利用する側、Mainクラスではクラスを「new」することでインスタンスが生成され、
* 「オブジェクトはそれらの属性とメソッドを実際に持つインスタンス」であること確認してください。
*
* @author tomo-sato
*/
public class Main {
    /** 起動引数の計算種別:1.足し算 */
    public static final int ARGS_TYPE_ADDITION = 1;
    /** 起動引数の計算種別:2.引き算 */
    public static final int ARGS_TYPE_SUBTRACTION = 2;
    /** 起動引数の計算種別:3.掛け算 */
    public static final int ARGS_TYPE_MULTIPLICATION = 3;
    /** 起動引数の計算種別:4.割り算 */
    public static final int ARGS_TYPE_DIVISION = 4;
    /**
    * mainメソッドでは、定義した加減乗除のクラスを起動引数により切り替え計算処理を行います。
    *
    * @param args 起動引数
    */
    public static void main(String[] args) {
        // 起動引数より、計算種別を取得します。
        int type = Integer.parseInt(args[0]);
        int i = 10;
        int j = 3;
        // Calculatorインターフェースに計算クラスを代入し、計算を行うサンプル。
        Calculator calculator = null;
        // オブジェクト指向の主要な特徴「4.ポリモーフィズム」により実装されている為、
        // 「異なるクラスのオブジェクトを一貫して操作できます。」に該当し、
        // 各計算クラスは、Calculatorインターフェースの型に代入することが出来ます。
        switch (type) {
            case ARGS_TYPE_ADDITION:
                System.out.println("足し算クラスAdditionのインスタンスを生成します。");
                calculator = new Addition(i, j);
                break;
            case ARGS_TYPE_SUBTRACTION:
                System.out.println("引き算クラスSubtractionのインスタンスを生成します。");
                calculator = new Subtraction(i, j);
                break;
            case ARGS_TYPE_MULTIPLICATION:
                System.out.println("掛け算クラスMultiplicationのインスタンスを生成します。");
                calculator = new Multiplication(i, j);
                break;
            case ARGS_TYPE_MULTIPLICATION:
                System.out.println("割り算クラスDivisionのインスタンスを生成します。");
                calculator = new Division(i, j);
                break;
            default:
                throw new IllegalArgumentException("起動引数に誤りがあります。(1.足し算、2.引き算、3.掛け算、4.割り算)");
        }
        int ans = calculator.getAnswer();
        System.out.println("計算結果は、ans=" + ans + " です。");
        // CalculatorBaseクラスに計算クラスを代入し、計算を行うサンプル。
        CalculatorBase calculatorBase = null;
        // オブジェクト指向の主要な特徴「4.ポリモーフィズム」により実装されている為、
        // 「異なるクラスのオブジェクトを一貫して操作できます。」に該当し、
        // 各計算クラスは、CalculatorBaseインターフェースの型に代入することが出来ます。
        switch (type) {
            case ARGS_TYPE_ADDITION:
                System.out.println("足し算クラスAdditionのインスタンスを生成します。");
                calculatorBase = new Addition(i, j);
                break;
            case ARGS_TYPE_SUBTRACTION:
                System.out.println("引き算クラスSubtractionのインスタンスを生成します。");
                calculatorBase = new Subtraction(i, j);
                break;
            case ARGS_TYPE_MULTIPLICATION:
                System.out.println("掛け算クラスMultiplicationのインスタンスを生成します。");
                calculatorBase = new Multiplication(i, j);
                break;
            case ARGS_TYPE_DIVISION:
                System.out.println("割り算クラスDivisionのインスタンスを生成します。");
                calculatorBase = new Division(i, j);
                break;
            default:
                throw new IllegalArgumentException("起動引数に誤りがあります。(1.足し算、2.引き算、3.掛け算、4.割り算)");
        }
        int ans2 = calculatorBase.getAnswer();
        System.out.println("計算結果は、ans2=" + ans2 + " です。");
        // オブジェクト指向の主要な特徴「3.継承」により実装されている為、
        // 「継承により、既存のクラスの属性とメソッドを再利用できます。」
        int ans3 = calculatorBase.getAnswerWithLogging();
        System.out.println("計算結果は、ans3=" + ans3 + " です。");
    }
}


Calculator.java

package lesson_05;

/**
* 計算を行うインターフェースです。
*
* このインターフェースは、計算手段を提供します。
* インターフェースは具体的な実装を持ちません。
* 設計図のような役割を持ち、「このクラスを実装する」と言うことは
* 必ず実装クラスでは「int getAnswer()」を実装するという事が約束されます。
* (※実装しない場合コンパイルエラーとなります。)
*
* @author tomo-sato
*/
public interface Calculator {
    /**
    * 計算結果を返します。
    *
    * これはオブジェクト指向の主要な特徴「4.ポリモーフィズム」に該当します。
    * このインターフェースを実装するクラスは、必ず「getAnswer()」メソッドを定義することになり、
    * 「同じ名前のメソッドが異なるクラスで異なる振る舞いをすることができる」に該当することになります。
    *
    * @return このインターフェースの実装クラスでは、計算結果を返す機能を提供する。
    */
    int getAnswer();
}


CalculatorBase.java

package lesson_05;

/**
* 計算を行うクラスの基底です。
*
* このクラスは、抽象クラスであり、具体的な計算処理を行いません。
* 継承したクラスで具体的な計算処理を行います。
* これはオブジェクト指向の主要な特徴「3.継承」に該当します。
* このクラスを継承するクラスは、必ず「getAnswer()」メソッドを実装することになります。
* また、継承するクラスでは、「getAnswerWithLogging()」が継承される為
* そのクラスでもログ出力機能を利用することが可能となります。
*
* 尚、このクラスは、abstractクラスとして抽象化されています。
* これはオブジェクト指向の主要な特徴「5.抽象化」に該当します。
*
* @author tomo-sato
*/
abstract public class CalculatorBase implements Calculator {
    /**
    * このクラスでは、プロパティ「int i」と、「int j」を保持します。
    * この変数は、インスタンス生成時に指定された引数の値を保持します。
    * また、修飾子はprotectedとして定義されている為、参照するためには下記制約が付与されます。
    * 1.同じクラス内: protectedプロパティは、そのクラス内から直接アクセスできます。
    * 2.サブクラス内: protectedプロパティは、同じパッケージ内のサブクラスからアクセスできます。
    * また、異なるパッケージ内のサブクラスでもアクセスできますが
    * その場合はサブクラスを通じてアクセスする必要があります。
    * 3.同じパッケージ内: 同じパッケージ内のクラスは、protectedプロパティに直接アクセスできます。
    * 4.他のパッケージ内の非サブクラス: protectedプロパティは
    * 他のパッケージ内の非サブクラスからは直接アクセスできません。
    * ただし、非サブクラスが同じパッケージ内のクラスを通じてアクセスすることはできます。
    *
    * これはオブジェクト指向の主要な特徴「2.カプセル化」に該当します。
    * 「データと操作をクラス内でカプセル化し、クラスの外部からは
    * アクセス制御を施すことができる」に該当します。
    * 保持した値が不用意に代入された無い為、適宜修飾子を指定する必要があります。
    * データの操作は、参照用のメソッドや、設定用のメソッドを用意して行うようにします。
    * getter/setterと呼ばれます。
    */
    /** 第一引数 */
    protected int i = 0;
    /** 第二引数 */
    protected int j = 0;
    /**
    * コンストラクタ。
    *
    * 2つの引数「int i」と、「int j」を指定し、このクラスを初期化します。
    * また、「int i」と、「int j」は、計算処理に使用されることを目的としています。
    *
    * @param i 第一引数
    * @param j 第二引数
    */
    public CalculatorBase(int i, int j) {
        this.i = i;
        this.j = j;
    }
    /**
    * 抽象メソッド「getAnswer()」の前後でログを出力します。
    *
    * @return 計算結果を返します。
    */
    public int getAnswerWithLogging() {
        System.out.println("このクラスで保持している値:i=" + this.i + ", j=" + this.j);
        int ans = this.getAnswer();
        System.out.println("このクラスで計算した結果:ans=" + ans);
        return ans;
    }
    /**
    * CalculatorインターフェースのgetAnswer()を実装しつつ、
    * 且つ、このクラスでは具体的な実装方法を持たない為、抽象メソッドgabstractで定義している。
    *
    * このクラスを継承するクラスでは、「getAnswer()」をオーバーライドし
    * 具体的な計算ロジックを実装する必要がある。
    * 継承によっても同じ名称のメソッドが異なるクラスで異なる振る舞いを実現できるため、
    * これもオブジェクト指向の主要な特徴「4.ポリモーフィズム」に該当します。
    *
    * @return 計算結果を返します。
    */
    @Override
    abstract public int getAnswer();
}


Addition.java

package lesson_05;

/**
* 足し算を行うクラス。
*
* このクラスは、CalculatorBaseクラスを継承しています。
* つまり、CalculatorBaseクラスで定義されたプロパティ「int i」と、「int j」を持ち、
* メソッド「getAnswerWithLogging()」を持つクラスになります。
* これはオブジェクト指向の主要な特徴「3.継承」に該当します。
* 「既存のクラスを拡張し、新しいクラスを作成できます。」、「継承により、既存のクラスの属性とメソッドを再利用できます。」
*
* また、CalculatorBaseクラスで定義された抽象メソッド「getAnswer()」の具体的な実装をしています。
* これはオブジェクト指向の主要な特徴「4.ポリモーフィズム」に該当します。
* 「同じ名前のメソッドが異なるクラスで異なる振る舞いをすることができます。」
*
* @author tomo-sato
*/
public class Addition extends CalculatorBase {
    /**
    * 計算に使用する値を保持し、このクラスのインスタンスを生成します。
    *
    * @param i 第一引数
    * @param j 第二引数
    */
    public Addition(int i, int j) {
        // スーパークラスCalculatorBaseのコンストラクタを参照し、iとjを初期化します。
        super(i, j);
    }
    /**
    * このクラスに保持された値を使用し、足し算を行います。
    *
    * @return i + j の結果を返します。
    */
    @Override
    public int getAnswer() {
        return this.i + this.j;
    }
}


Subtraction.java

package lesson_05;

/**
* 引き算を行うクラス。
*
* ※詳細の解説は、Additionクラスを参照ください。
*
* @author tomo-sato
*/
public class Subtraction extends CalculatorBase {
    /**
    * 計算に使用する値を保持し、このクラスのインスタンスを生成します。
    *
    * @param i 第一引数
    * @param j 第二引数
    */
    public Subtraction(int i, int j) {
        super(i, j);
    }
    /**
    * このクラスに保持された値を使用し、引き算を行います。
    *
    * @return i - j の結果を返します。
    */
    @Override
    public int getAnswer() {
        return this.i - this.j;
    }
}


Multiplication.java

package lesson_05;

/**
* 掛け算を行うクラス。
*
* ※詳細の解説は、Additionクラスを参照ください。
*
* @author tomo-sato
*/
public class Multiplication extends CalculatorBase {
    /**
    * 計算に使用する値を保持し、このクラスのインスタンスを生成します。
    *
    * @param i 第一引数
    * @param j 第二引数
    */
    public Multiplication(int i, int j) {
        super(i, j);
    }
    /**
    * このクラスに保持された値を使用し、掛け算を行います。
    *
    * @return i * j の結果を返します。
    */
    @Override
    public int getAnswer() {
        return this.i * this.j;
    }
}


Division.java

package lesson_05;

/**
* 割り算を行うクラス。
*
* ※詳細の解説は、Additionクラスを参照ください。
*
* @author tomo-sato
*/
public class Division extends CalculatorBase {
    /**
    * 計算に使用する値を保持し、このクラスのインスタンスを生成します。
    *
    * @param i 第一引数
    * @param j 第二引数(※ゼロは指定できません。)
    */
    public Division(int i, int j) {
        super(i, j);
        if (j == 0) {
            throw new IllegalArgumentException("ゼロで割ることはできません。");
        }
    }
    /**
    * このクラスに保持された値を使用し、割り算を行います。
    *
    * @return i / j の結果を返します。
    */
    @Override
    public int getAnswer() {
        return this.i / this.j;
    }
}

サトウ先生によるワンポイントアドバイス

このサンプルコードを通じて、オブジェクト指向プログラミングの基本的な概念を解説しました。
以下は、今後の学習を進める上でのアドバイスになります。

  1. 基本概念の理解
    オブジェクト指向プログラミングは、プログラムを設計し、問題を解決するための強力な方法です。
    基本概念(クラス、オブジェクト、カプセル化、継承、ポリモーフィズム、抽象化)をしっかり理解しましょう。
  2. 実践重視
    知識を実践に移すことが重要です。
    新しいクラスやオブジェクトを設計し、実際のプロジェクトで活用してみましょう。
  3. コードの可読性
    オブジェクト指向プログラミングは、コードの構造が重要です。
    わかりやすく、保守性の高いコードを書くために、適切なクラスやメソッドの設計に時間をかけましょう。
  4. 例外処理
    例外処理はプログラムの信頼性を高めます。
    エラーハンドリングについても学習し、適切な例外処理を行う練習をしましょう。
  5. デバッグ
    プログラムのデバッグはスキルの一部です。
    バグを見つけて修正する経験を積んで、デバッグのスキルを向上させましょう。
  6. 設計とリファクタリング
    プログラムの設計は初期段階から重要ですが、リファクタリング(コードの再構築)も大切です。
    プロジェクトが進むにつれて、コードの改善と最適化に時間を割きましょう。
  7. 他のコードを読む
    他のプログラマーのコードを読むことは学習の一環です。
    オープンソースプロジェクトやコミュニティで共有されているコードを見て、他のプログラマーの設計とスタイルを学びましょう。
  8. 継続的な学習
    プログラムは常に進化しています。
    新しい技術やベストプラクティスについても学習を続け、自分自身を向上させる姿勢を持ちましょう。

最後に、プログラミングは実践と継続的な学習が鍵です。
エラーを恐れず、試行錯誤を続けながら、オブジェクト指向プログラミングのスキルを着実に向上させていきましょう!

メンバー募集のお知らせ

dcWORKSでは一緒に働く仲間を常時募集しています。私たちと一緒に人々の心を動かすものを創りませんか?
今なら就職お祝い金プレゼントキャンペーンを実施中です。ぜひご応募ください。

興味がある方は以下からお気軽にご応募ください。

募集要項はこちら

ITエンジニアを目指したい方へ

dcWORKSではIT業界の慢性的な人材不足解消・20代人材のキャリアプラン創造のためEngineers Gateを運営しています。
Engineers Gateでは無料でITエンジニア(プログラマー・インフラエンジニア)の育成型就職サポートを実施しております。

・IT業界に興味があるがどうやったら就職できるのか
・ITの勉強方法がわからない
・独学で限界を感じている方 etc

上記の様な悩みをお持ちの方は以下からお気軽に相談してください。

詳細はこちら

お気軽に
お問い合わせください。

CONTACT