Menu
Coddy logo textTech

インスタンス変数

CoddyのLuaジャーニー「Object Oriented Programming」セクションの一部 — レッスン 12/70。

オブジェクトが内部データをどのように管理するかに焦点を当てた、新しい章へようこそ。これまでにクラスとインスタンスの作成方法を学びました。次は、各インスタンスが独自のステート(状態)をどのように保存し、管理するかを探求しましょう。

インスタンス変数は、単一のオブジェクトのみに属するフィールドです。メソッドやコンストラクタの中で self.something に値を代入すると、そのデータはその特定のインスタンスの中にのみ保持されます。同じクラスの他のオブジェクトは、それぞれ独自のコピーを持ちます。

これは、テーブルのような複雑なデータを保存する際に特に重要になります。各キャラクターが異なるアイテムを所持するインベントリシステムを考えてみましょう:

local Inventory = {}
Inventory.__index = Inventory

function Inventory:new()
    local instance = {}
    setmetatable(instance, self)
    instance.items = {}  -- 各インスタンスは独自の空のテーブルを持ちます
    return instance
end

function Inventory:addItem(item)
    table.insert(self.items, item)
end

重要な行は instance.items = {} です。コンストラクタ内で新しい空のテーブルを作成することで、すべてのインベントリオブジェクトが独自の個別のリストを受け取ります。もし代わりに items をクラスのテーブル自体で定義した場合、すべてのインスタンスが誤って同じテーブルを共有してしまいます。つまり、あるキャラクターに剣を追加すると、全員がそれを持つことになってしまいます!

local bag1 = Inventory:new()
local bag2 = Inventory:new()

bag1:addItem("Sword")
bag2:addItem("Potion")

print(#bag1.items)  -- 出力: 1
print(#bag2.items)  -- 出力: 1

各インベントリは独自のコレクションを保持します。このパターンは、インスタンスが独立して変化するデータ(リスト、カウンター、またはそのオブジェクトに固有の可変状態など)を追跡する必要がある場合に常に適用されます。

challenge icon

チャレンジ

簡単

各カートが独自の製品リストを保持する ShoppingCart システムを構築しましょう。これにより、インスタンス変数がオブジェクト間でデータをどのように分離して保持するかを実証します。一方のカートにアイテムを追加しても、もう一方のカートには影響しません!

コードは2つのファイルに分けて構成します:

  • ShoppingCart.lua: 各インスタンスが独自の products テーブルを持つ ShoppingCart クラスを定義します。以下を含めてください:
    • 各カートに対して空の製品リストを初期化する :new() コンストラクタ
    • このカートのリストに製品を追加する :addProduct(productName) メソッド
    • 各製品を個別の行に出力する :listProducts() メソッド
  • main.lua: ShoppingCart モジュールを読み込み、2つの独立したカートを作成します。それぞれのカートに異なる製品を追加して、それらが独立した在庫を維持していることを証明してください。

4つの入力を受け取ります:

  1. カートAに追加する1つ目の製品
  2. カートAに追加する2つ目の製品
  3. カートBに追加する1つ目の製品
  4. カートBに追加する2つ目の製品

メインファイルでの手順:

  1. 2つのショッピングカート cartAcartB を作成します
  2. 最初の2つの製品を cartA に追加します
  3. 最後の2つの製品を cartB に追加します
  4. Cart A: と出力し、次にカートAの製品をリスト表示します
  5. Cart B: と出力し、次にカートBの製品をリスト表示します

例えば、入力が AppleBreadMilkEggs の場合、出力は以下のようになります:

Cart A:
Apple
Bread
Cart B:
Milk
Eggs

ヒント:重要なのは、コンストラクタ内で products を新しい空のテーブルとして初期化することです。これにより、すべてのカートで1つのテーブルを共有するのではなく、各カートインスタンスが独自の個別のリストを持つようになります。

チートシート

インスタンス変数とは、単一のオブジェクトに排他的に属するフィールドのことです。メソッドやコンストラクタの中で self.something に値を代入すると、そのデータはその特定のインスタンスの中にのみ存在することになります。

インスタンス変数を作成するには、インスタンスごとに新しいテーブルを使用して、コンストラクタ内でそれらを初期化します。

local Inventory = {}
Inventory.__index = Inventory

function Inventory:new()
    local instance = {}
    setmetatable(instance, self)
    instance.items = {}  -- 各インスタンスは独自の空のテーブルを持ちます
    return instance
end

function Inventory:addItem(item)
    table.insert(self.items, item)
end

重要なのは instance.items = {} です。コンストラクタ内で新しい空のテーブルを作成することで、すべてのオブジェクトが独自の個別のデータを受け取ることが保証されます。もし items をクラス・テーブル自体で定義してしまうと、すべてのインスタンスが同じテーブルを共有することになります。

local bag1 = Inventory:new()
local bag2 = Inventory:new()

bag1:addItem("Sword")
bag2:addItem("Potion")

print(#bag1.items)  -- 出力: 1
print(#bag2.items)  -- 出力: 1

各インスタンスは、独自のコレクションを独立して保持します。このパターンは、リスト、カウンター、またはインスタンスごとに変化するその他のデータなど、オブジェクトに固有のあらゆるミュータブル(可変)な状態に適用されます。

自分で試してみよう

-- ShoppingCartモジュールを読み込む
local ShoppingCart = require('ShoppingCart')

-- 入力を読み込む
local product1 = io.read()
local product2 = io.read()
local product3 = io.read()
local product4 = io.read()

-- TODO: 2つのショッピングカート(cartAとcartB)を作成する

-- TODO: product1とproduct2をcartAに追加する

-- TODO: product3とproduct4をcartBに追加する

-- TODO: "Cart A:"と表示し、cartAの商品を一覧表示する

-- TODO: "Cart B:"と表示し、cartBの商品を一覧表示する
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

Object Oriented Programmingのすべてのレッスン