前提・実現したいこと
ファイル読み込み→変換→ファイル出力を行うような処理をTempleteMethodパターンで作成しようとしています。
以下の様な処理で考えているのですが、何点か気になる点があり、クラス設計に問題があるのか悩んでいます。
問題点をご指摘いただけないでしょうか。
前提
- Dto1,Dto2やConvertDto1,ConvertDto2の項目は全く違うもので抽象化はできません
気になる点
- パラメータがobject型なので具象クラスを作成する人間が毎回object型を適切な型に変換し、後続の処理を行う必要がある
- Dtoが抽象化できないのでAbstractClassのメソッドのパラメータ・返り値、メンバ変数の定義が
軒並みobject型になってしまい、変数やメソッドの用途がイマイチ分かりづらい - そもそもTemplateMethodパターンのサンプルで、抽象メソッドに返り値を持たせる
ようなものをあまり見かけません、ということはTemplateMethodパターンを使うこと自体が間違っている?
処理
C#
1 public abstract class AbstractClass 2 { 3 protected abstract IEnumerable<object> ReadFile(); 4 5 protected abstract IEnumerable<object> Convert(IEnumerable<object> readDtos); 6 7 protected abstract void WriteFile(IEnumerable<object> convertDtos); 8 9 private IEnumerable<object> _readDtos; 10 11 private IEnumerable<object> _convertDtos; 12 13 public void Run() 14 { 15 // ファイルを読み込みDtoに設定 16 this._readDtos = this.ReadFile(); 17 18 // 読み込んだデータを変換 19 this._convertDtos = this.Convert(this._readDtos); 20 21 // ファイル出力 22 this.WriteFile(this._convertDtos); 23 } 24 } 25 26 public class ConcreteClass1 : AbstractClass 27 { 28 protected override IEnumerable<object> ReadFile() 29 { 30 // ファイル読み込み 31 32 // 読み込み内容をDtoの配列で返却 33 34 return IEnumerable<dto1> 35 } 36 37 protected override IEnumerable<object> Convert(IEnumerable<object> readDtos) 38 { 39 // パラメータがobjectなのでDto1に変換しなければならない 40 var dtos = (IEnumerable<Dto1>)readDtos; 41 42 // readDto→convertDto変換処理 43 44 return IEnumerable<convertDto1>; 45 } 46 47 protected override void WriteFile(IEnumerable<object> convertDtos) 48 { 49 // パラメータがobjectなのでConvertDto1に変換しなければならない 50 var dtos = (IEnumerable<ConvertDto1>)convertDtos; 51 52 // ファイル書き込み処理 53 } 54 } 55 56 public class ConcreteClass2 : AbstractClass 57 { 58 protected override IEnumerable<object> ReadFile() 59 { 60 return IEnumerable<dto2>; 61 } 62 63 protected override IEnumerable<object> Convert(IEnumerable<object> readDtos) 64 { 65 var dtos = (IEnumerable<Dto2>)readDtos; 66 67 return IEnumerable<convertDto2>; 68 } 69 70 protected override void WriteFile(IEnumerable<object> convertDtos) 71 { 72 var dtos = (IEnumerable<ConvertDto2>)convertDtos; 73 } 74 }
何がしたいのかよくわかりません。ここにコードで表されている処理を具体的に書き換えることはできますが、それが実際にやりたいことに当てはめられるかどうかわかりません。
具体的にファイルをどう変換したいのか(エンコーディングを変えるのか文字列を読み取って置換するのかなど)書いた方がいいと思います。ただ「変換したい」というだけなら何百種類も方法があります。
また、Read, Convert, Write の三種類のメソッドに分けることが良いのかどうかもここからは読み取れません。object としか言えないなら分ける必要はありません。メソッドは一つで済むでしょう。Stream から Read し、Stream に Write するメソッドはわざわざ実装しなくても既にあるからです。
具象クラス側の処理内容も踏まえてじゃないと
正確なアドバイスができない、ということですね。
質問の意図としては、もう少し大まかな感じで考えていました。
Read, Convert, Writeには、何かしら具象クラスに記述しないといけない
個別の処理があり、ReadではConvertに渡すデータを取得している。
という前提で、その様な処理の場合TemplateMethodパターンを使うべきなのか
他に何か良いパターンがあるのか
また、TemplateMethodを使いもっと分かりやすく記述する方法があるのか
という事をご教授いただきたいと考えていました。
踏まえてというか、三種類のメソッドを作るメリットを説明できますか?
無いように見えます。
ならばテンプレート自体が間違っていますので、別の方法をとるべきです。
ここに書かれたコードだけ見ると、全く無意味なことをしているようにしか見えません。無意味でない可能性を探るために具体的なやりたいことを聞いたのですが、無いならばやはり無意味です。
必ず3種類のメソッドが必要か?と言われるとそうではないと思いますが
単純に大きな1メソッドで3メソッドの分の処理を記述するより
3メソッドに分けた方が処理が分かりやすくなるのでは?と思っています。
private でいいじゃないですか。
継承できないし外から使えないものは全部 private でいいんですよ。互換性がないということは継承できないということでしょう?
ここで継承させるべきものは Run ただ一つだけです。
そりゃジェネリックを使って形だけは無理やり継承させることはできますが、それは単なる継承のための継承で、無意味な作業です。手段と目的をはき違えています。
「何を継承させるか」が設計です。
例えばReadの処理を考えると、イメージしていたのは具象クラスのReadメソッドではCsv、Excel、DB等から
データを読み込んで、Dtoに値を設定する事を考えていました。
この処理って出来ないことは無いと思いますが、AbstractClassにprivateで書かない方が良くないですか?
その場合は public メソッドのシリアライザを作るべきです。Convert は不要です。デコードした時に共通のデータが得られるはずです。いや、共通のデータにデコードすべきです。
むむむ、シリアライザですか、、、ちょっとググってきます