Размер видео: 1280 X 720853 X 480640 X 360
Показать панель управления
Автовоспроизведение
Автоповтор
その都度リファクタリングするのは、本当に大事ですね。進んでからだとその修正も大変ですし、開発自体に時間が掛かる可能性が高くなりますし。私も現在、しばらく放置していた独自変数を少し手直しをしてますが、やはり面倒ですね。出来る限り、書き直す部分は極力少ない組み方をしていますが、使いまわす事も多いものは、それだけで時間と精神を削られます。
大変ですが、リファクタリングは念入りにやっていきたいですね。ここをいかにわかりやすく作れるかが今後の活動にも響いてくるので。ただし時間をかけすぎるとモチベも下がっていくので、システムの根幹部分に関しては次回で決めに行きたいところです。
-こんにちは!初めて動画を見させていただいたんですが、--各`Component`の初期化はそれぞれのコンストラクタで行えばいいように思うのですが、`ComponentFactory`の存在意義は何でしょうか?--各`Component`のコンストラクタに初期化を任せて、コンストラクタを直接呼ぶ形にすれば`ComponentFactory`がまるまる不要になる気がするんですが。--何かファクトリを介さないといけない理由はあるのでしょうか?-過去動画も見て理解しました。ランタイムで生成する`Component`を切り替えるためにファクトリが挟まってるんですね。
良〜い質問ですね〜実はComponentのコンストラクタで初期化の処理をすべて任せるのは、作り方によっては可能だと思います。ComponentFactoryを実装している利点は、ComponentやGameObjectの初期化に関わる役割を削減できるという点が挙げられます。ComponentFactoryを実装しておくことで、Componentそのものを登録しておく機能など、比較的「Componentの外部に任せたい作業」を実装することができます。要するに「プログラムの構成がわかりやすくなる」というのが、結局のところの利点だと思います。(たぶんComponentですべてを管理する場合、static変数やstaticメソッドを使って同様のことが可能だと思います)あとnew演算子やdelete演算子をなるべく直接使いたくなかったり、作ったComponentをまとめて管理できたりすると便利だなーとか考えたりして、作っておいた方がいろいろ便利だなと実装に踏み切りました!たぶん他にも実装方法はいろいろあります!
@@SukakiyoLab 返信ありがとうございます!たしかにランタイムで設定ファイルからもろもろ読み込むときなんかは結局条件分岐書いてインスタンス化対象を切り替えることになるので`Component`生成のもろもろを担うクラスがあった方が楽そうですね。いや~失念してました
いえいえ自分もまだまだプログラミング初心者なので、めちゃくちゃ助かります!(やっべ、よくよく考えたら「ちょっと前までめちゃくちゃ読みにくいソースコード書いてた」なんて言えない)
@@SukakiyoLab (注:長文です。mdエディタに張り付けて読むこと推奨)いえいえ私も全然初心者ですので...一応最近勉強しているなりの提案を書いておきますファクトリ側に各`Component`ごとの定義を書いちゃってて、具体的すぎる気がするので、その部分をDI的な構造にして外に出すといいんじゃないかと思います。`Component`が増える度に`ComponentFactory`の中身をいじりに行くのは面倒ですし。C++わからんのでTypescriptで適当に書いた例: (型定義がクソ雑魚なのは許して...)```typescript/* Static members */interface ComponentConstructor{ tryCreate(option: unknown): Component | null; /* Add definitions below if you want. */}/* Non-static members */interface Component{ /* Add definitions below if you want. */}/* おまじない (deep.tacoskingdom.com/blog/181) */const Implements = () => (ctor: U) => ctor;class ComponentFactory{ private readonly registered_components: Set = new Set(); public register(component: ComponentConstructor): void{ this.registered_components.add(component); } public createComponent(option: unknown): Component | null{ for (const component of this.registered_components) { const result = component.tryCreate(option); if (result) return result; } return null; }}@Implements()class StringComponent{ private readonly option: string; public constructor(option: string) { this.option = option; } public static tryCreate(option: unknown): StringComponent | null{ return typeof option === "string" ? new StringComponent(option) : null; }}@Implements()class NumberComponent{ private readonly option: number; public constructor(option: number) { this.option = option; } public static tryCreate(option: unknown): NumberComponent | null{ return typeof option === "number" ? new NumberComponent(option) : null; }}const factory = new ComponentFactory();factory.register(StringComponent);factory.register(NumberComponent);console.log(factory.createComponent("Hi!")); // Output: StringComponent { option: 'Hi!' }console.log(factory.createComponent(42)); // Output: NumberComponent { option: 42 }console.log(factory.createComponent(false)); // Output: null```(staticなメンバーをinterfaceに書こうとするとややこしくなるのはJS由来のTSの仕様なので気にしてはいけない)これで`ComponentFactory#createComponent(option)`したときに`ComponentFactory`に`register()`した`Component`の`tryCreate(option)`を片っ端から呼び出して、成功したらその結果を返す挙動になります。(全て失敗したら`null`を返す。一応雑に動作確認済み。)ただ、こういう定義をすると当然型による補完が効かなくなります。動的に返すインスタンスを切り替えるのに使うだけなら大した問題にはならないと思いますが、ハードコードした`Component`を使いたい場合にファクトリから呼び出すのが補完が効かなくてうざかったりするかもしれません。その場合は`Component`のオプションに補完を効かせるためだけの関数なんかを定義するといいかもしれません。(もっといい方法あるかも...)具体的な実装方法ですが、多分C++で実装しようと思ったら、- `register()`あるいは`new`時に`tryCreate()`相当の関数を関数ポインタで受けて、適当なコレクションに保存 - C++でできるか知らんが、上のTSコードは(インスタンスではなく)クラス自体を受け取るような処理をしている。 もしできるならその方が分かりやすいコードになるかも。- `createComponent()`時に保存した関数ポインタ達を列挙して片っ端から与えられたオプションと共に呼び出すという実装になるんじゃないかと思います。ご参考までに。
その都度リファクタリングするのは、本当に大事ですね。
進んでからだとその修正も大変ですし、開発自体に時間が掛かる可能性が高くなりますし。
私も現在、しばらく放置していた独自変数を少し手直しをしてますが、やはり面倒ですね。出来る限り、書き直す部分は極力少ない組み方をしていますが、使いまわす事も多いものは、それだけで時間と精神を削られます。
大変ですが、リファクタリングは念入りにやっていきたいですね。ここをいかにわかりやすく作れるかが今後の活動にも響いてくるので。
ただし時間をかけすぎるとモチベも下がっていくので、システムの根幹部分に関しては次回で決めに行きたいところです。
-こんにちは!初めて動画を見させていただいたんですが、-
-各`Component`の初期化はそれぞれのコンストラクタで行えばいいように思うのですが、`ComponentFactory`の存在意義は何でしょうか?-
-各`Component`のコンストラクタに初期化を任せて、コンストラクタを直接呼ぶ形にすれば`ComponentFactory`がまるまる不要になる気がするんですが。-
-何かファクトリを介さないといけない理由はあるのでしょうか?-
過去動画も見て理解しました。ランタイムで生成する`Component`を切り替えるためにファクトリが挟まってるんですね。
良〜い質問ですね〜
実はComponentのコンストラクタで初期化の処理をすべて任せるのは、作り方によっては可能だと思います。
ComponentFactoryを実装している利点は、ComponentやGameObjectの初期化に関わる役割を削減できるという点が挙げられます。ComponentFactoryを実装しておくことで、Componentそのものを登録しておく機能など、比較的「Componentの外部に任せたい作業」を実装することができます。
要するに「プログラムの構成がわかりやすくなる」というのが、結局のところの利点だと思います。(たぶんComponentですべてを管理する場合、static変数やstaticメソッドを使って同様のことが可能だと思います)
あとnew演算子やdelete演算子をなるべく直接使いたくなかったり、作ったComponentをまとめて管理できたりすると便利だなーとか考えたりして、作っておいた方がいろいろ便利だなと実装に踏み切りました!
たぶん他にも実装方法はいろいろあります!
@@SukakiyoLab
返信ありがとうございます!
たしかにランタイムで設定ファイルからもろもろ読み込むときなんかは結局条件分岐書いてインスタンス化対象を切り替えることになるので
`Component`生成のもろもろを担うクラスがあった方が楽そうですね。いや~失念してました
いえいえ自分もまだまだプログラミング初心者なので、めちゃくちゃ助かります!
(やっべ、よくよく考えたら「ちょっと前までめちゃくちゃ読みにくいソースコード書いてた」なんて言えない)
@@SukakiyoLab
(注:長文です。mdエディタに張り付けて読むこと推奨)
いえいえ私も全然初心者ですので...
一応最近勉強しているなりの提案を書いておきます
ファクトリ側に各`Component`ごとの定義を書いちゃってて、具体的すぎる気がするので、その部分をDI的な構造にして外に出すといいんじゃないかと思います。
`Component`が増える度に`ComponentFactory`の中身をいじりに行くのは面倒ですし。
C++わからんのでTypescriptで適当に書いた例: (型定義がクソ雑魚なのは許して...)
```typescript
/* Static members */
interface ComponentConstructor{
tryCreate(option: unknown): Component | null;
/* Add definitions below if you want. */
}
/* Non-static members */
interface Component{
/* Add definitions below if you want. */
}
/* おまじない (deep.tacoskingdom.com/blog/181) */
const Implements = () => (ctor: U) => ctor;
class ComponentFactory{
private readonly registered_components: Set = new Set();
public register(component: ComponentConstructor): void{
this.registered_components.add(component);
}
public createComponent(option: unknown): Component | null{
for (const component of this.registered_components) {
const result = component.tryCreate(option);
if (result) return result;
}
return null;
}
}
@Implements()
class StringComponent{
private readonly option: string;
public constructor(option: string) {
this.option = option;
}
public static tryCreate(option: unknown): StringComponent | null{
return typeof option === "string" ? new StringComponent(option) : null;
}
}
@Implements()
class NumberComponent{
private readonly option: number;
public constructor(option: number) {
this.option = option;
}
public static tryCreate(option: unknown): NumberComponent | null{
return typeof option === "number" ? new NumberComponent(option) : null;
}
}
const factory = new ComponentFactory();
factory.register(StringComponent);
factory.register(NumberComponent);
console.log(factory.createComponent("Hi!")); // Output: StringComponent { option: 'Hi!' }
console.log(factory.createComponent(42)); // Output: NumberComponent { option: 42 }
console.log(factory.createComponent(false)); // Output: null
```
(staticなメンバーをinterfaceに書こうとするとややこしくなるのはJS由来のTSの仕様なので気にしてはいけない)
これで`ComponentFactory#createComponent(option)`したときに`ComponentFactory`に`register()`した`Component`の`tryCreate(option)`を片っ端から呼び出して、成功したらその結果を返す挙動になります。
(全て失敗したら`null`を返す。一応雑に動作確認済み。)
ただ、こういう定義をすると当然型による補完が効かなくなります。
動的に返すインスタンスを切り替えるのに使うだけなら大した問題にはならないと思いますが、ハードコードした`Component`を使いたい場合にファクトリから呼び出すのが補完が効かなくてうざかったりするかもしれません。
その場合は`Component`のオプションに補完を効かせるためだけの関数なんかを定義するといいかもしれません。
(もっといい方法あるかも...)
具体的な実装方法ですが、多分C++で実装しようと思ったら、
- `register()`あるいは`new`時に`tryCreate()`相当の関数を関数ポインタで受けて、適当なコレクションに保存
- C++でできるか知らんが、上のTSコードは(インスタンスではなく)クラス自体を受け取るような処理をしている。
もしできるならその方が分かりやすいコードになるかも。
- `createComponent()`時に保存した関数ポインタ達を列挙して片っ端から与えられたオプションと共に呼び出す
という実装になるんじゃないかと思います。
ご参考までに。