今回は構造体の最終問題で「構造体の更新」!
構造体って言ってるけど、JSだとクラスでやってるから…。
問題概要
「名前 年齢 誕生日 出身地」の生徒情報を以下の形式でまとめる↓
User{
nickname : 名前
old : 年齢
birth : 誕生日
state : 出身地
}
🔍クラスの人数と全員の情報、更新についての情報が与えられるので、入力に従って名前を更新した後のクラスのメンバーの情報を出力せよ。
入力例:
3 2
mako 13 08/08 nara
taisei 16 12/04 nagano
megumi 14 11/02 saitama
2 taihei
3 megu
出力例:
mako 13 08/08 nara
taihei 16 12/04 nagano
megu 14 11/02 saitama
🔺OK例:changeNameなし
const rl = require('readline').createInterface({input:process.stdin});
const lines = [];
rl.on('line', (input) => {
lines.push(input);
});
rl.on('close', () => {
const [N, K] = lines[0].split(' ').map(Number);
class User{
constructor(nickname, old, birth, state){
this.nickname = nickname;
this.old = Number(old);
this.birth = birth;
this.state = state;
}
format(){
return `${this.nickname} ${this.old} ${this.birth} ${this.state}`;
}
}
const users = lines
.slice(1, N+1)
.map(line =>
new User(...line.split(' '))
);
for(let i = N + 1; i console.log(user.format()));
});
「構造体の更新」ていう「問題名」と「入出力例」を見ただけでやることが理解できたから、問題文を読んでいなかったので、
changeName が作られていない(ぼくはおばかさんです)
(´;ω;`)今更ながら…。
function User(nickname, old, birth, state) {
this.nickname = nickname;
this.old = Number(old);
this.birth = birth;
this.state = state;
}
User.prototype.format = function() {
return `${this.nickname} ${this.old} ${this.birth} ${this.state}`;
};
- コンストラクタ関数で、
Userオブジェクト を作成するためのテンプレート -
User.prototypeにformatメソッド を追加することで、Userの全インスタンス がこのメソッドを共有。
もしかしたら、こういう風に書くのがJSで構造体っぽいものの書き方だったのかもとネットで見つけちゃった(´;ω;`)
※一応これでclassで書いたバージョンと同じ動きをするし、テストケースは全部通過する。
✅ ✨OK例:
const rl = require('readline').createInterface({ input: process.stdin });
const lines = [];
rl.on('line', (input) => {
lines.push(input);
});
rl.on('close', () => {
const [N, K] = lines[0].split(' ').map(Number);
// Userクラス(設計図)
class User {
constructor(nickname, old, birth, state) {
this.nickname = nickname;
this.old = Number(old);
this.birth = birth;
this.state = state;
}
// ✅ 名前を変更するインスタンスメソッド
changeName(newName) {
this.nickname = newName;
}
// ✅ 表示用
format() {
return `${this.nickname} ${this.old} ${this.birth} ${this.state}`;
}
}
// 生徒データをまとめて作る
const users = lines.slice(1, N + 1).map(line => {
const [nickname, old, birth, state] = line.split(' ');
return new User(nickname, old, birth, state);
});
// 名前の更新を適用する
for (let i = N + 1; i console.log(user.format()));
});
✅ポイント
| 部分 | 役割 |
|---|---|
| changeName(newName) | インスタンスの nickname を新しい名前で上書きするメソッド |
| users[index - 1] | 1-based のインデックスを 0-based に直す |
| .map() | User のインスタンスをまとめて作る |
| .format() | 出力用のメソッド |
✅ そもそも何がしたいの?
生徒データ(User)は
あとで名前を変えることがある(更新)。
user.nickname = "新しい名前";
// ↑ これだけでも変わる
プロパティ(データ)を外から直接書き換えOK だから、nickname を直接上書きしても動く。
✅ でも changeName を作ると何が良いの?
| changeName がある場合 | 直接プロパティを変える場合 |
|---|---|
| 名前を変えるときに必ず同じ方法になる | どこでも自由に書き換えられる |
| 「変え方のルール」が1か所にまとまる | ルールがバラバラになる可能性がある |
| 追加処理を後から入れられる | 書き換えが散らばると変更が面倒 |
- 小さな学習スクリプトでは、
changeNameはなくてもOK。 - 大きめのプロジェクトやチーム開発では、専用のメソッドでカプセル化するのがベター。
つまり、 changeName の利点は「保守性」や「再利用性」など。
「どう変えるか」は内部で守りたいから、外には触らせない、が鉄則!
✅ カプセル化って何?
カプセル化 =
「データ(プロパティ)と、そのデータを操作する方法(メソッド)をひとまとめにして、外から勝手に触れないようにする仕組み」
✅ なぜカプセル化するの?
- データを勝手に書き換えられたくない
- ルールを守らせたい
- 修正を一か所で済ませたい
- プログラムが壊れにくくなる
など。
✅ 今回の changeName はカプセル化?
実は JS のクラスで nickname は public(公開)だから、
外から user.nickname = “新しい名前” で変えられちゃう。
だから厳密には 完全なカプセル化 にはなってないけど、
「名前を変えるときは必ず changeName を通す」と決めるだけでも カプセル化の第一歩 !
✅ 本気でカプセル化するなら?
JavaScript では # を使って
プライベートフィールド にすると外から直接いじれなくなる。
class User {
#nickname; //ここでプライベートフィールドを宣言
constructor(name) {
this.#nickname = name; //クラス内部ではアクセス・代入できる
}
changeName(newName) {
this.#nickname = newName;
}
get nickname() {
return this.#nickname; //外に値を出したい時はメソッドを通す
}
}
const u = new User("mako");
console.log(u.nickname); // mako
u.changeName("megumi");
console.log(u.nickname); // megumi
u.#nickname = "hacked"; // ❌ エラー!外からは触れない
✅ ポイントまとめ
| 用語 | 内容 |
|---|---|
| カプセル化 | データと操作をセットで管理し、勝手に変えさせない |
| メソッド | データを安全に操作する「窓口」 |
| プライベートフィールド(#) | 完全に外から触れなくする方法(ES2020〜) |
✨今回のコードをカプセル化
const rl = require('readline').createInterface({input:process.stdin});
const lines = [];
rl.on('line', (input) => {
lines.push(input);
});
rl.on('close', () => {
const [N, K] = lines[0].split(' ').map(Number);
class User{
#nickname;
#old;
#birth;
#state;
constructor(nickname, old, birth, state){
this.#nickname = nickname;
this.#old = Number(old);
this.#birth = birth;
this.#state = state;
}
changeName(newName){
this.#nickname = newName;
}
format(){
return `${this.#nickname} ${this.#old} ${this.#birth} ${this.#state}`;
}
}
const users = lines
.slice(1, N+1)
.map(line =>
new User(...line.split(' '))
);
for(let i = N + 1; i console.log(user.format()));
});
-
nicknameなどの#は「プライベートフィールド」で、クラスの外からは直接触れない。
→user.#nickname = ‘mako’は外でやるとエラーになる! - 代わりに
changeName()というインスタンスメソッドを使って名前を変更する。
→ これが「カプセル化」の例。 - 出力も
format()を通じて、内部のフィールドを安全に取り出している。 -
constructorの中でも#で宣言したものをthis.#で扱ってる。
⚠️実行環境の注意
- paizaでは使えなかった
- Node.js で
#を使うのは v12.0.0 以降。 -
node -vでバージョンを確認して、必要ならアップデートしてね!
僕は paiza で動かなかったので、Node.js をインストールして VSCode で書いて、ターミナルで実行して試した!