interfaceについて

はじめに

こんにちは、コーディング課の一回生のHiroakiです。

昨日に続いて今日もアドカレを書かせていただきます。今回はinterfaceについて僕の中で一つの結論が出たので、それについて書こうと思います。(入部してからコードを書き始めた人間ですので、温かい目で読んでください)

簡単にinterfaceの書き方と使い方

interfaceの書き方について

using UnityEngine;

public interface IMove
{
    void Move(Vector3 direction);
}

これがinterfaceです。いつもclassと書いているところにintefaceと書きます。

中にはメソッドしか書くことができず、実装の中身も書くことはできません。

書くことができるのは、戻り値 メソッド名(引数);これだけです。

嘘です。

実際はプロパティを実装した変数や、実装も書くことができます。しかし、変数はともかく、実装を書くということはあまりないと思うので気にする必要はありません。

使い方について

using UnityEngine;

public class Player:IMove
{
    private float _speed;
    Transform _playerPosition;

    public Player(Transform playerPosition,float speed)
    {
        _playerPosition = playerPosition;
        _speed = speed;
    }
    public void Move(Vector3 direction)
    {
        _playerPosition.position += _speed * direction.normalized * Time.deltaTime;
    }
    public void Attack()
    {
        //攻撃の実装
    }
}
public class PlayerManager:MonoBehaviour
{
    [SerializeField] private float _speed;
    [SerializeField] private Transform _playerPosition;
    IMove mover;
    void Start()
    {
        mover = new Player(_playerPosition,_speed);
    }
    void Update()
    {
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");
        Vector3 moveAmount=new Vector3(horizontal,0,vertical);
        mover.Move(moveAmount);
    }
}

まずはPlayerクラスの解説からしていきます。

PlayerクラスはIMoveを継承しています。実際に書いてみたら分かると思うのですが、Moveを実装していないうちに、IMoveを継承するとMoveを実装していませんというエラーが出ると思います。これは継承したinterfaceのメソッドの実装を書かなければならないという決まりがあるからです。interfaceがよく契約だと言われる所以はここにあるのではないかと僕は思っています。

次はPlayerMangerについてです。

PlayerManagerPlayerを使うクラスなのですが、直接Playerを参照に取らずにinterface越しで参照を持って使っています。

interfaceを継承したクラスは自分のクラスの型以外にinterfaceの型に入ることもできます。

ここではprivate IMove mover;で型の宣言を行い、Startの中でmover = new Player(_playerPosition,_speed);を行い、IMove型のmoverの中にIMoveを継承したPlayer型のインスタンスを入れています。

moverIMoveの型に入っているので、Playerを持ってはいますが、IMoveの実装しか知りません、なのでmover.Attack();みたいなことはできません。

これでinterfaceの書き方と使い方の説明を終わります。

interfaceとは何か

ここからは僕がたどり着いたinterfaceの結論について書いていこうと思います。

interfaceとは何を公開するかを定義するものです。

アクセス修飾詞というものを知っているでしょうか?使っていない人はいないと思います。publicとかprivateのことです。classで変数やメソッドにアクセス修飾詞を書かなかった場合は自動的にアクセス修飾詞が割り当てられるのですが、何が割り当てられるか知っていますか?

正解はprivateです。なので、classにおいていちいちprivateを書く必要が実際はないということです。

では、interfaceはどうでしょうか?知っていますか?

正解はpublicです。

なぜか?それはinterfaceは他のクラスに参照をとってもらうためにあるからです。

interfaceを継承するだけではそれは実装の強制でしかないのですが、interfaceを参照をとることにより公開する範囲を制限することができます。

interfaceはクラスの継承とは違い多重継承することができます。それがinterfaceで公開する範囲を制限できることにつながります。

コード例を一つだそうと思います。


public interface IUse
{
    void Use();
}

public interface IRefill
{
    void Refill();
}

public class Syouyu:IRefill,IUse
{
    private int Amount=10;
    public void Refill()
    {
        Amount = 10;
        Debug.Log("醤油を補充した");
    }
    public void Use()
    {
        if (Amount <= 0)
        {
            Debug.Log("醤油の中身がない");
            return;
        }
        Amount--;
        Debug.Log($"醤油の残り{Amount}");
    }
}

public class MayoSource : IRefill, IUse
{
    private int Amount = 10;
    public void Refill()
    {
        Amount = 10;
        Debug.Log("マヨネーズを補充した");
    }
    public void Use()
    {
        if (Amount <= 0)
        {
            Debug.Log("マヨネーズの中身がない");
            return;
        }
        Amount--;
        Debug.Log($"マヨネーズの残り{Amount}");
    }
}
using UnityEngine;

public class Master
{
    IUse _currentUse;
    public Master(IUse use)
    {
        _currentUse = use;
    }

    public void Use()
    {
        _currentUse.Use();
    }
    public void Change(IUse nextUse)
    {
        _currentUse = nextUse;
    }
}

public class Maid
{
    IRefill _currentRefill;
    public Maid(IRefill refill)
    {
        _currentRefill = refill;
    }
    public void Refill()
    {
        _currentRefill.Refill();
    }
    public void Change(IRefill nextRefill)
    {
        _currentRefill = nextRefill;
    }
}

public class TestMono:MonoBehaviour
{
    private Syouyu _syouyu;
    private MayoSource _mayoSource;
    private Master _master;
    private Maid _maid;
    void Start()
    {
        _syouyu = new Syouyu();
        _mayoSource = new MayoSource();
        _master = new Master(_syouyu);
        _maid = new Maid(_syouyu);
    }
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            _master.Use();
        }
        if (Input.GetKeyDown(KeyCode.B))
        {
            _maid.Refill();
        }
        if (Input.GetKeyDown(KeyCode.Space))
        {
            _master.Change(_mayoSource);
            _maid.Change(_mayoSource);
        }
        if (Input.GetKeyDown(KeyCode.LeftShift))
        {
            _master.Change(_syouyu); 
            _maid.Change(_syouyu);
        }
    }
}

注目するところはSyouyuクラスやMayoSourceクラスがIUseIRfillを多重継承して、masterクラスはIUseを参照にとり、maidクラスはIRefillを参照に取ってる点です。

多重継承を行うことにより、参照の取り方を分けることができます。それにより、masterクラスはIUseを参照に取り、interfaceを挟んでSyouyuなどを参照に取っていますが、Refillを行うことはできません、maidクラスも同様です。このように多重継承とinterface越しに参照を持つことで、クラスの責務を明確にし必要以上の機能をそもそも使うことができなくするようにできます。

終わりに

interfaceについて僕の見解を述べさせていただきました。

まだコーディングを初めて一年もたっていないので、これが本当に正しいかはわかりませんので話半分で受け取ってもらったらうれしいです。

コメントなどで、それは違うよ!みたいな指摘を待ってます。

ここまで読んでくれてありがとうございました。

コメントを残す

CAPTCHA