プログラミング

ピカチュウから学ぶオブジェクト指向 〜クラス継承編〜 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"...

 

注目の記事

保守っていくらで何をする?フリーランスWebエンジニア対象!保守業務について解説します
フリーランス
システムエンジニア,フリーランス,プログラマー
保守っていくらで何をする?フリーランスWebエンジニア対象!保守業務について解説します

  サイトの保守を頼まれたけど、具体的に何をやればいいの?   フリーランスの、特に独学やスクール上がりでなった人にとっては保守は何をすれば良いかわからないという悩みを抱えている人は多いのではないでしょうか。 システム開発会社や制作会社にいた人でも、どのぐらいの金額で請け負うのが良いか検...

飲食店がホームページを作る5つのメリット 〜デメリットについても解説します〜
マーケティング
Google AdSense,SEO
飲食店がホームページを作る5つのメリット 〜デメリットについても解説します〜

  お店のホームページを作りたい   そういった飲食店の方は意外にも多く、イタリアンや居酒屋、日本料理店など種類も様々です。   今回のテーマは「飲食店がホームページを作る上で知っておきたいメリットとデメリットについて」です。   食べログやホットペッパーなどといったグルメサイトが主流とな...

HTMLの基本設計「よくわかるSEO対策」構造化(マークアップ)編
SEO対策
HTML,SEO,マークアップ
HTMLの基本設計「よくわかるSEO対策」構造化(マークアップ)編

  HTML5がリリースされて随分経ちました。 それにより多くのタグが増え、それにより今までデザインを目的と使用されていたものが「構造化」という本来の役割を持たされるようになりました。    構造化は、目に見えにくい部分です   それぞれの役割を理解していなければ、せっかくきれいなサ...

【Laravel】論理削除対応型existsバリデーションの実装方法
プログラミング
Laravel,PHP
【Laravel】論理削除対応型existsバリデーションの実装方法

  Laravelでは多くのバリデーションが提供されていますが、論理削除を使用している場合はそのままでは使えないものが複数あります。 今回は紐付けをする際に存在チェックで使用するexistsのソフトデリート対応のバリデーションを実装する方法をご紹介します。     カスタムバリデーションの追加   存...

会話スキルに自信がない人必見!会議やミーティングで失敗しないコツとは
雑記
Skype,Zoom
会話スキルに自信がない人必見!会議やミーティングで失敗しないコツとは

  話下手なので会議で置物状態 Zoomミーティングは発言しづらい 会議の時間が退屈で無駄   そんな悩みや不満を抱えている方はいませんか? 確かに会議は退屈ですし、話下手な人からすれば嫌な時間の1つかも知れませんね。 ですが、立ち回りのポイントを3つ押さえておくだけで、無駄・退屈だと感じて...

20代の独立が成功のカギ【学生→フリーランス・起業は危険です】
フリーランス
フリーランス,独立,起業
20代の独立が成功のカギ【学生→フリーランス・起業は危険です】

  終身雇用のほとんどが崩壊している今、学生の頃から独立や起業を考えている人は多いですが、安易な決断は危険です。 独立するには早すぎるのも良いとは言えず、また遅すぎることもそれなりにリスクです。 SNSやメディアでも学生起業などが騒がれていますが、それに影響されてしまうのはかなり危険なことです...

PHPポケモン「オートローダー編」16
プログラミング
PHP,PHPポケモン,ポケモン
PHPポケモン「オートローダー編」16

  今までは技やタイプを一括requireという荒業で対応していましたが、フシギダネ系列の技を実装した際に「こんなん全部読み込んでられるか!」と流石になったので、簡易ながらオートローダーを実装していきます。 そして、実装したらしたで色々と問題も浮かび上がってきたので、このあたりは回を進めて行きなが...

捕獲処理実装編 PHPポケモン 80
プログラミング
PHP,PHPポケモン,ポケモン
捕獲処理実装編 PHPポケモン 80

捕獲処理の作成 前回モンスターボールのクラスを作成したので、今回は捕獲判定までの一連の処理を仕上げていきます。サービス自体は他のアイテムと一緒にするためItemServiceを呼び出し、その中で使用されたアイテムを判断して分岐を作ります。   バトル中のアイテムサービス(/App/Services/Battle/ItemService.ph...

カテゴリ

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

タグ

5G Adobe AfterEffects AI ajax amazon Animate api artisan atom Automator AWS Bluetooth CSS CVR description EC-CUBE4 ECショップ ESLint Facebook feedly foreach fortify function Google Google AdSense Honeycode htaccess HTML IEEE 802.11ax Illustrator Instagram IoT JavaScript jetstream 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 独立 神戸 福祉 秘密鍵 翻訳 自己啓発 英語 見積書 計算機 認証 読書 起業 迷惑メール 配列 銀の弾丸 集客 雑学力