プログラミング

ピカチュウから学ぶオブジェクト指向 〜クラス継承編〜 2

PHP PHPポケモン オブジェクト指向 ポケモン
ピカチュウから学ぶオブジェクト指向 〜クラス継承編〜 2

 

ピカチュウから学ぶオブジェクト指向の第2弾オブジェクトの継承についてです。

前回作成したピカチュウクラスを使用するので、もし基礎的な内容を学習したい人は、以下の記事を参考にしてください。

ピカチュウから学ぶオブジェクト指向 〜入門編〜 1 ピカチュウから学ぶオブジェクト指向 〜入門編〜 1

オブジェクト指向とは  オブジェクト指向プログラミング https://ja.wikipedia.org/wiki/オブジェクト指向プログラミング オブジェクト指向プログラミングとは、互いに密接な関連性を持つデータとメソッドをひとつにまとめてオブジェクトとし、それぞれ異なる性質と役割を持たせたオブジェクトの様々な定...

 

オブジェクトの継承が理解できれば、複雑で規模の大きなシステムを構築することができるようになります。また、重複する記述が減らせることで、保守性なども格段に向上して作業負担も軽減されるため、ぜひこの機会に覚えておきましょう。

 

 

オブジェクトの継承とは

 

オブジェクトの継承(PHP.net

 

継承するということは、子となるクラスが親クラスのメソッドやプロパティを使用できるようにすることを意味します。前回作成したピカチュウクラスで考えてみましょう。

<?php
// ピカチュウ
class Pikachu
{

    private $name = 'ピカチュウ';
    public $nickname;
    private $move = [
        'でんきショック',
        'なきごえ',
    ];
 
    // クラス呼び出し時(インスタンス作成時)に自動で最初に実行される処理
    public function __construct($nickname=null)
    {
        $this->nickname = $nickname ?? $this->name;
    }
 
    // 正式名称を取得する
    public function getName()
    {
        return $this->name;
    }
 
    // 覚えている技の一覧を取得する
    public function getMove()
    {
        return $this->move;
    }
 
}

 

ニックネームを付ける機能や、名前や技の一覧を取得するというメソッドは、他のポケモン(ヒトカゲやゼニガメ等)でも同じくできなければなりません。しかし、これをポケモンを作るたびに準備するのは大変ですね。初代だけでも151匹分必要になります。

現在はクラス内の記述も少ないので問題ありませんが、よりゲームに近づけるとなれば更に多くの機能が必要になりますし、もし一部変更しなければならないようなことになれば、151匹分修正をかける必要があるのです。

 

そうならないためにも、共通して使える機能は親クラス上に記述して、そのクラスを継承して各ポケモンで使用できるようにします

 

 

「ポケモン」クラスを作成(親クラス)

 

ピカチュウの親となるのはどういったクラスでしょうか?

細かくカテゴライズしていけば、より良い継承先が見つかるかも知れませんが、大きな区分で見れば「ポケモン」というカテゴリに属しています。

 

では、Pokemonというクラスを新しく作成しましょう。

<?php
// ポケモン
class Pokemon
{
 
    protected $nickname;
   
    protected function setNickname($nickname)
    {
        $this->nickname = $nickname ?? $this->name;
    }
 
    // 正式名称を取得する
    public function getName()
    {
        return $this->name;
    }
   
    // ニックネームを取得する
    public function getNickname()
    {
        return $this->nickname;
    }
 
    // 覚えている技の一覧を取得する
    public function getMove()
    {
        return $this->move;
    }
 
}

 

他のポケモンでも共通する、ニックネームのプロパティ($nickname)正式名称を取得するメソッド(getName)技の一覧を取得するメソッド(getMove)をピカチュウのクラスから移動してきました。

ピカチュウのコンストラクタで実行していたニックネームを付ける処理は、setNicknameとして新しくメソッドを作成しました。ニックネームのプロパティもポケモンクラスに移動させたので、こちらも取得用のメソッドとしてgetNicknameを作成しています。

※アクセス修飾子のprotectedについては後ほど解説します

 

 

クラスを継承する

 

ポケモンというクラスを継承するためには、子に当たるPikachuクラスにその旨を記述する必要があります。ピカチュウクラスまでを含めたコード全体を見てみましょう。 

<?php
// ポケモン
abstract class Pokemon
{
 
    protected $nickname;
   
    protected function setNickname($nickname)
    {
        $this->nickname = $nickname ?? $this->name;
    }
 
    // 正式名称を取得する
    public function getName()
    {
        return $this->name;
    }
   
    // ニックネームを取得する
    public function getNickname()
    {
        return $this->nickname;
    }
 
    // 覚えている技の一覧を取得する
    public function getMove()
    {
        return $this->move;
    }
 
}

// ピカチュウ
class Pikachu extends Pokemon
{
  
    protected $name = 'ピカチュウ';
    protected $move = [
        'でんきショック',
        'なきごえ',
    ];
 
    // クラス呼び出し時(インスタンス作成時)に自動で最初に実行される処理
    public function __construct($nickname=null)
    {
        $this->setNickname($nickname);
    }
 
}
 
$pikachu = new Pikachu;
echo $pikachu->getNickname();
 
$pikachu1 = new Pikachu('ピカイチ');
echo $pikachu1->getNickname();

 

echoでの出力結果は以下の通りです。 

# 出力結果
ピカチュウ
ピカイチ

 

引数でニックネームを指定しなければ「ピカチュウ」、指定すれば「ピカイチ」と問題なく出力されていますね。

 

では、1つずつ確認してみましょう。まずはピカチュウのクラスからです。

class Pikachu extends Pokemon

 

クラス名の横に、新しくextends Pokemonという記述が追加されました。これがPokemonというクラスを親として継承するという意味になります。

 

次にコンストラクタ内の部分を確認してみましょう。

public function __construct($nickname=null)
{
    $this->setNickname($nickname);
}

 

親(Pokemon)が持つニックネームの書き込み用メソッド(setNickname)を使用しています。親の持つメソッドは、自クラスが持つメソッドを使用するように$this->メソッド(orプロパティ)と記述すれば呼び出すことができます

 

次にポケモンのクラスを見てみましょう。

abstract class Pokemon

 

classの前に新しくabstractと追加されました。これはクラスの抽象化です。

 

親クラスであるポケモン(Pokemon)を、ピカチュウのようにインスタンス化(実体化)されてしまっては困りますよね。ポケモンというポケモンは存在しないので当然です。そうならないために用意されているのがクラスの抽象化(abstract)です。これを付与することで、継承元(親クラス)としてだけ使用できるようになります。ポケモンのクラスは、あくまでピカチュウなどに共通するメソッドやプロパティを管理するためだけの親クラスでなければならないのです。

 

 

アクセス修飾子(protected

 

他にも変更されたポイントがあります。それがアクセス修飾子であるprotectedです。

ピカチュウクラス内のnameプロパティについて見てみましょう。

protected $name = 'ピカチュウ';

 

こちらは前回privateにしていました。理由は、外部からアクセスされて別のものに変更されてしまっては困るからです。ですがprivateにしてしまうと、ピカチュウクラス内からでしかアクセスが出来ません

 

ポケモンクラスを見てみましょう。

public function getName()
{
    return $this->name;
}

 

正式名称を取得するための「getName」というメソッドでピカチュウクラス内のnameプロパティにアクセスしています。もしprivateであれば、親クラスのポケモンクラスからもアクセスが出来ません。

外部からアクセスされては困る、しかし親クラスなど継承先や継承元からはアクセス出来なければ困る、そんな都合の良い制限をかけるために準備されたのが「protected」なのです。

他にも、setNicknameなどのメソッドに対してもprotectedが用いられています。メソッドについても、外部からのアクセスは困るが、クラス内では有効可したい場合にはprotectedをつけて同じような制限を設けることが可能です。

 

クラスの継承を使うようになれば、publicprotectedprivateの3つを使い分ける必要があります。それぞれどのような時に使用するかを考えながら、適切なアクセス制限を設けるようにしておきましょう。

  • public
    クラス内、クラス外のどこからでもアクセスが可能
  • protected
    同じクラス及び子クラスからアクセスが可能
  • private
    同じクラス内からのみアクセスが可能

 

 

技を覚えさせる

 

それでは新しく増設した継承クラスであるPokemon新しいメソッドを追加してみましょう。今回は「技を覚えさせる」という機能を増設します。どのポケモンでも技を新しく覚えていくことができますね。そのため、これはピカチュウのクラスなどではなく、親となるポケモンのクラスへ記述します。

// 技を覚えさせる
public function setMove($move)
{
    $this->move[] = $move;
    // 技が4つを超過していれば、最初の技を削除
    if(count($this->move) > 4){
        unset($this->move[0]);
        // 採番
        $this->move = array_values($this->move);
    }
}

 

setMoveというメソッドを新しく追加しました。これは外部からアクセスして使用するためpublicになります。

引数で技を指定して、それを新たにmoveプロパティ内にセットします。ポケモンのゲームでは技を覚えていられる最大数が4つなので、それを超過していれば一番上から消していきます。

 

それでは新しく「10万ボルト」「でんこうせっか」「かみなり」という3つの技を追加してみましょう。いかがコード全体図になります。

<?php
// ポケモン
abstract class Pokemon
{
 
    protected $nickname;
   
    protected function setNickname($nickname)
    {
        $this->nickname = $nickname ?? $this->name;
    }
 
    // 正式名称を取得する
    public function getName()
    {
        return $this->name;
    }
   
    // ニックネームを取得する
    public function getNickname()
    {
        return $this->nickname;
    }
 
    // 覚えている技の一覧を取得する
    public function getMove()
    {
        return $this->move;
    }
   
    // 技を覚えさせる
    public function setMove($move)
    {
        $this->move[] = $move;
        // 技が4つを超過していれば、最初の技を削除
        if(count($this->move) > 4){
            unset($this->move[0]);
            // 採番
            $this->move = array_values($this->move);
        }
    }
 
}

// ピカチュウ
class Pikachu extends Pokemon
{
  
    protected $name = 'ピカチュウ';
    protected $move = [
        'でんきショック',
        'なきごえ',
    ];

    // クラス呼び出し時(インスタンス作成時)に自動で最初に実行される処理
    public function __construct($nickname=null)
    {
        $this->setNickname($nickname);
    }
 
}
 
$pikachu = new Pikachu;
$pikachu->setMove('10万ボルト');
$pikachu->setMove('でんこうせっか');
$pikachu->setMove('かみなり');
 
var_export($pikachu->getMove());

 

$this->getMove()の出力結果は以下の通りです。

# 出力結果
array (
  0 => 'なきごえ',
  1 => '10万ボルト',
  2 => 'でんこうせっか',
  3 => 'かみなり',
)

 

順番に3つの技が追加されました。4つを超過したので、一番上にあった「でんきショック」が消えています。期待通りの結果ですね。

 しかしこのままでは、ピカチュウが覚えられないような技を追加出来てしまいますし、技が重複してしまう可能性があります。

 

これでは困りますので、重複チェックと覚えられる技の制限を加えましょう。まずは覚えられる技のリストをピカチュウのクラスに追記します。

// ピカチュウ
class Pikachu extends Pokemon
{
  
    protected $name = 'ピカチュウ';
    protected $move = [
        'でんきショック',
        'なきごえ',
    ];
    protected $move_list = [
        'でんきショック',
        'なきごえ',
        '10万ボルト',
        'かみなり',
        'でんこうせっか',
    ];
 
    // クラス呼び出し時(インスタンス作成時)に自動で最初に実行される処理
    public function __construct($nickname=null)
    {
        $this->setNickname($nickname);
    }
 
}

 

覚えられる技はポケモンによって異なります。なので、これはポケモンクラスではなく各ポケモンのクラスに対して割り振ります。また、外部からアクセスして変えられてしまっては困るので、外部からは不可、継承クラスからはアクセスが可能なprotectedを付与しておきましょう。

 

次に、覚えられる技と重複チェックをポケモンクラス内のsetMoveメソッドへ追記します。

// 技を覚えさせる
public function setMove($move)
{
    // 重複チェック
    if(in_array($move, $this->move, true)){
        echo $move.'はすでに覚えています';
        return;
    }

    // 覚えられる技かチェック
    if(!in_array($move, $this->move_list, true)){
        echo $move.'は覚えられません';
        return;
    }

    $this->move[] = $move;

    // 技が4つを超過していれば、最初の技を削除
    if(count($this->move) > 4){
        unset($this->move[0]);
        // 採番
        $this->move = array_values($this->move);
    }
}

 

まずは重複チェックです。

if(in_array($move, $this->move, true)){
    echo $move.'はすでに覚えています';
    return;
}

 

in_arrayを使用して、現在覚えている技($this->move)に覚えようとしているものが存在しているかをチェックしています。もしあればエラーメッセージを出力して、その時点で処理を終了させるためにreturnを使います。

 

次に覚えられる技かチェックをします。

if(!in_array($move, $this->move_list, true)){
    echo $move.'は覚えられません';
    return;
}

 

同じようにin_arrayを使用して$this->move_list内に技があるかどうかをチェックしています。今回はリスト内に技がなければエラーメッセージを出す必要があるので、!in_arrayで逆の判定をしています。こちらも同じように、エラーメッセージを出せばreturnで処理を終了させます。

 

それでは確認してみましょう。

<?php
// ポケモン
abstract class Pokemon
{
 
    protected $nickname;
  
    protected function setNickname($nickname)
    {
        $this->nickname = $nickname ?? $this->name;
    }
 
    // 正式名称を取得する
    public function getName()
    {
        return $this->name;
    }
   
    // ニックネームを取得する
    public function getNickname()
    {
        return $this->nickname;
    }
 
    // 覚えている技の一覧を取得する
    public function getMove()
    {
        return $this->move;
    }
   
    // 技を覚えさせる
    public function setMove($move)
    {
        // 重複チェック
        if(in_array($move, $this->move, true)){
            echo $move.'はすでに覚えています';
            return;
        }

        // 覚えられる技かチェック
        if(!in_array($move, $this->move_list, true)){
            echo $move.'は覚えられません';
            return;
        }

        $this->move[] = $move;

        // 技が4つを超過していれば、最初の技を削除
        if(count($this->move) > 4){
            unset($this->move[0]);
            // 採番
            $this->move = array_values($this->move);
        }
    }
 
}
 
// ピカチュウ
class Pikachu extends Pokemon
{
  
    protected $name = 'ピカチュウ';
    protected $move = [
        'でんきショック',
        'なきごえ',
    ];
    protected $move_list = [
        'でんきショック',
        'なきごえ',
        '10万ボルト',
        'かみなり',
        'でんこうせっか',
    ];
 
    // クラス呼び出し時(インスタンス作成時)に自動で最初に実行される処理
    public function __construct($nickname=null)
    {
        $this->setNickname($nickname);
    }
 
}
 
$pikachu = new Pikachu;
$pikachu->setMove('かえんほうしゃ');
$pikachu->setMove('でんきショック');
$pikachu->setMove('10万ボルト');
 
var_export($pikachu->getMove());

 

出力結果は以下の通りです。

# 出力結果
かえんほうしゃは覚えられません
でんきショックはすでに覚えています
array (
  0 => 'でんきショック',
  1 => 'なきごえ',
  2 => '10万ボルト',
)

 

最初にセットしようとした「かえんほうしゃ」はピカチュウが覚えられる技のリストに含まれていませんので、エラーメッセージが返ってきました。次にセットしようとした「でんきショック」はすでに覚えているので、同様にエラーメッセージが返ってきました。

最後にセットした10万ボルトは、覚えられるリスト内にも含まれており、現在は覚えていないので無事覚えることができました。

 

このようにして、共通して使いたい機能は継承クラスとして準備することで、他のポケモンを用意した際には独自の設定を追加するだけで同じように使用することができるのです。

 

 

まとめ

 

いかがだったでしょうか。

今回は「【PHP】ピカチュウから学ぶオブジェクト指向 〜クラス継承編〜」をご紹介しました。

オブジェクト(クラス)の継承ができるようになれば、管理はよりしやすくなり、大幅な変更などが生じても比較的対応がしやすくなります。また、ポケモンのように種類が多いようなものを管理するためには向いています。

PHPを学習中の方で、クラスを使ったシステムやサイトづくりを考えている人は、ぜひ参考にしてくださいね。

 

第3回はコチラ(レベルシステムの導入)

ピカチュウから学ぶオブジェクト指向 〜レベルシステム導入編〜 3 ピカチュウから学ぶオブジェクト指向 〜レベルシステム導入編〜 3

  大人気?シリーズ「ピカチュウから学ぶオブジェクト指向」、今回はレベルシステムの導入編です。   第1回(基礎編)、第2回(クラス継承編)で作成したPokemonとPikachuのクラスを使用するので、最初から学習したい人はぜひご参考ください。 [shortcode_url_og url="https://s-yqual.com/blog/1035"...

 

注目の記事

スキル習得で挫折しないための3ステップ
雑記
デザイン,ブログ,プログラミング
スキル習得で挫折しないための3ステップ

  せっかく高い教材を買ったのに、気づいたら積んでしまっている・・・   そんな悩み、あなたには有りませんか? 今回は、そんな方のための勉強方法をテーマに説明していきます。 正しい勉強方法を身に着けていれば、どういったメリットがあるのでしょうか? 三日坊主になりにくい いざという...

これをしてはいけません!「よくわかるSEO対策」エンジニアのための基礎知識編
SEO対策
htaccess,HTML,JavaScript,SEO,エンジニア,プログラミング
これをしてはいけません!「よくわかるSEO対策」エンジニアのための基礎知識編

  近年ではWebサイト制作会社や個人事業主はかなり増えてきました。 会社によっては力を入れている部分は異なり、主に「デザイン」「機能(システム)」「SEO」という3つに分けられます。 飽和しているのでは無いかと囁かれている中、この3つすべてを揃えた事業者は意外と少なく、個人となれば更に少なくなります。...

【PHP】可変長引数とは「点(ドット)3つ」 多次元連想配列の検索
プログラミング
PHP
【PHP】可変長引数とは「点(ドット)3つ」 多次元連想配列の検索

可変長引数とは  引数の数が決まっていない、状況に応じて複数の引数を指定したいときに、関数の引数で指定する点(ドット)3つのことです。PHP5.6以降で使用することができます。  PHPマニュアル 可変長引数リスト https://www.php.net/manual/ja/functions.arguments.php#functions.variable-arg-list ...

そらをとぶ&あなをほる編 PHPポケモン46
プログラミング
PHP,PHPポケモン,ポケモン
そらをとぶ&あなをほる編 PHPポケモン46

チャージ中の回避技 以前は「ロケットずつき」や「ソーラービーム」をサンプルとしてチャージ技を実装しましたが、今回は少し特別な効果をもったチャージ技を実装します。それが「そらをとぶ」と「あなをほる」です。これらは初代ポケモンでも重宝される技であり、チャージ中に相手からの攻撃を回避することができま...

進化の石編(実装)PHPポケモン103
プログラミング
PHP,PHPポケモン,ポケモン
進化の石編(実装)PHPポケモン103

アイテムによる進化 前回作成した、進化アイテムによる構成の続きです。   進化アイテムとして「かみなりのいし」、ピカチュウの進化判定を作成したので、アイテムの使用から進化処理までを作成、実装します。   今回作成する処理は以下の2点です。 アイテムの使用判定 進化画面への移管   それ...

英語できたらブログで稼げる 【ネオコピペ】
ライティング
ブログ,翻訳,英語
英語できたらブログで稼げる 【ネオコピペ】

  2010年に楽天が社内公用語の英語化を宣言したのは、多くの人が耳にしたことがあるでしょう。 私からすると、「英語ができることは強み」だという考えから、「これからは英語ができないと行きていけないのでは?」と考えを変えさせられるような、そんな出来事でした。   多言語できるデメリットを見つけるのは難...

【税金は貰うもの】知って得する!お金を稼ぐための実践的な3要素
マーケティング
【税金は貰うもの】知って得する!お金を稼ぐための実践的な3要素

  「お金を稼ぐための基本的な3要素」に続く、「実践的な3要素」です。 前回は、単価、頻度、客数の関係を理解すれば、お金を稼ぐ仕組みがどのように成立しているのか、そしてどういったアプローチをすればお金を稼げるのかがわかりました。   今回は、直接お金に関係する実践的な3要素です。どう...

アウトプットのための3つの習慣【3対7を成立させよう】
雑記
アウトプットのための3つの習慣【3対7を成立させよう】

  インプットとアウトプットの比率は3対7がベストだと言われています。 しかし、簡単にできるインプットに比べて、アウトプットは習慣化させておくことが大切です。それができていない人の多くが、膨大に本を読んだり学習に取り組んでも身につかず、趣味のレベルで終わってしまうのです。   今回は、そんな...

カテゴリ

SEO対策 イベント デザイン ネットワーク ビジネスモデル フリーランス プログラミング マーケティング ライティング 動画編集 雑記

タグ

5G Adobe AfterEffects AI ajax amazon Animate api artisan atom Automator AWS Bluetooth CSS CVR description EC-CUBE4 ECショップ ESLint Facebook feedly foreach function Google Google AdSense Honeycode htaccess HTML IEEE 802.11ax Illustrator Instagram IoT JavaScript jQuery jQuery UI keyword LAN Laravel Linux MacBook MAMP meta MLM MySQL NoCode note OS OSI参照モデル Paypal Photoshop PHP phpMyAdmin PHPポケモン PremierePro rss SEO SEO対策 Sequel Pro Skype SNS SSH Symfony TCP/IP title Toastr Trait Twig Twitter UCC V系 WAN WebSub Wi-Fi wiki Windows WordPress XAMPP xml Xserver YouTube YouTuber Zoom アーティスト アウトプット アクセス層 アニメーション アフィリエイト イーブイ インターネット インプット エンジニア オブジェクト指向 お金配り クリック単価 クリック数 コミュニケーション能力 コロナ コンサルティング サムネイル システムエンジニア スタートアップ スタイルシート スパム データベース ディープフェイク デザイナー デザイン テレワーク ナンパ ニュース ネットワークモデル ノマドワーク バナー ピカチュウ ビジネス フィード フリーランス ブロガー ブログ プログラマー プログラミング プログラミング学習 プログラミング教育 プロトコル ホームページ制作 ポケモン マークアップ マーケティング メール リモートワーク レンダリング 三井住友 三宮 仕事依頼 児童デイ 児童デイサービス 児童発達支援 公開鍵 初心者 助成金 勉強法 営業 広告 広告収入 必勝マニュアル 放課後等デイサービス 朝活 楽天 深層学習 無線LAN 独立 神戸 福祉 秘密鍵 翻訳 自己啓発 英語 見積書 計算機 読書 起業 迷惑メール 配列 銀の弾丸 集客 雑学力