💬

カプセル化(アクセス修飾子 private, public, protected)

に公開

まえがき

chat-gptで作った学習ロードマップをプログラミング初学者が勉強する試みです
ロードマップはchat-gptを使用してますが、学習は公式のチュートリアルや技術系ブログなどを参考にしています
Goの復習も兼ねているのでGoとの違いについても言及します
今回はカプセル化(アクセス修飾子 private, public, protected)について内容をまとめていきます

アクセス修飾子(private, public, protected)の種類と役割

アクセス制御にはまず大きく分けて、最上位レベルとメンバーレベルのふたつがある

  1. 最上位レベル
    クラスのこと、サブクラスも最上位レベル
    ネストされたクラスはメンバーレベル
    最上位レベルではpublic、またはパッケージプライベート(修飾子無し)が使える

  2. メンバーレベル
    クラスのメンバーのこと
    フィールド、コンストラクタ、メソッド、ネストされたクラスなど
    メンバーレベルではpublicかパッケージプライベート(修飾子無し)に加えて
    private、protected、が使える

修飾子 同じクラス内から 同じパッケージから 別パッケージのサブクラスから グローバルから
public o o o o
protected o o o x
修飾子無し o o x x
private o x x x

基本的にアクセス制限は最も厳しいレベルを使用するべきである
例えば同じクラスでしか使用しないメンバーにprivate以外のアクセス制限をつけてもメリットは無い上に、意図しないアクセスはエラーやセキュリティホールになりえる

カプセル化とは?

概要

オブジェクト指向のプログラミングで重要な概念

目的

利用する際に不正な値を防ぎ、内部の実装を知らなくても安全に使えるようにする

実装方法

データの保護、内部実装の隠蔽、適切な制御、によって実装する

  • データの保護
    アクセス制御をすることで直接操作できないようにする
    アクセス装飾子を使う

  • 内部実装の隠蔽
    メソッドで内部のデータを操作することによって外部からは隠蔽され、内部の実装を知らなくても操作できるようにする

  • 適切な制御
    メソッドの中でデータの操作を制御することで、不正な値が使えないようにする

これら一連の実装をすることによってデータの安全性、コードの保守性、再利用性を高める

カプセル化をした実装

カプセル化
class Bank {
  // private修飾子をつけてクラス内でのみアクセスを許可する
  private int balance;

  // メソッドで操作することで実装を外部から隠蔽する
  int getBalance() {
    return balance;
  }

  // メソッドの中で操作を制御することで不正な値が使えないようにする
  void deposit(int amount) {
    if (0 < amount) {
      balance += amount;
    }
  }
  // メソッドの中で操作を制御することで不正な値が使えないようにする
  void withdraw(int amount) {
    if (0 < balance && amount < balance) {
      balance -= amount;
    }
  }
}

int => double のように仕様変更などがあっても、カプセル化をしていればクラス内を書き換えるだけで対応が可能になる
balanceが0以下にならないように、操作するたびにif文を使って制御する必要も無くなる

JavaとGoの違い

Goはアクセス修飾子がないので頭文字で公開範囲を区別する

  1. 頭文字が大文字
    パッケージ外部にも公開する
    外部からアクセスする際はimportでパッケージを指定する

  2. 頭文字が小文字
    パッケージ外部には公開しない
    importで読み込んでもアクセスできない

組み合わせて使うことで以下のように外部に公開しつつ、値はprivateに保つことができる

bank.go
package bank

// 構造体名の頭文字は大文字なのでパッケージ外部からアクセスできる(インスタンスの初期化など)
// フィールド名の頭文字は小文字なのでパッケージ外部からアクセスできない
type Bank struct {
	balance int
}

// メソッドは外部からアクセスしたいので頭文字が大文字
func (b *Bank)GetBalance() int {
	return b.balance
}
func (b *Bank)Deposit(amount int) {
	if amount > 0 {
		b.balance += amount
	}
}
func (b *Bank)Withdraw(amount int) {
	if amount > 0 && b.balance > amount {
		b.balance -= amount
	}
}

パッケージをmainとは別にすることでmainパッケージからのアクセスは頭文字が大文字のみ許可される
構造体Bankは大文字なのでアクセスできるが、フィールドbalanceは小文字なのでアクセスできない
なのでmainパッケージからインスタンスの初期化はできるが値を直接操作ができないようになる

他にもインターフェースを使ってパッケージ内からも操作の制限などはできるが、アクセス自体はできるため完全にカプセル化するならパッケージを分ける必要がある

まとめ

Javaのアクセス制御の方法やカプセル化の方法を学び、Goでの実装方法との違いを調べた

カプセル化がいまいちわかっていなかったが、改めて調べたらめちゃくちゃ必須の概念だった
Goで理解が浅かったのは日本語ドキュメント層の違いもありそう

おかげでGoでのカプセル化についても理解が深まったので、違いを比べながら復習するのは学習パフォーマンスが高めでよかったので続けようと思う
それと今回から、記事の構成をある程度フレームワークに基づいて書くようにしてみた
文章力皆無すぎるからね、がんばろうね

次は継承(extends)、多態性(オーバーライド)について学習します

初学者なので間違ってる部分があればご指摘いただけると嬉しいです

Discussion