回答編集履歴

3 追記

haru666

haru666 score 1519

2017/03/01 11:31  投稿

現状の作りで正しいと思います。
取りうる選択肢は以下の2つだけです。
・現状の形を維持する
・正常なデータのみが書き込み処理に到達するようにする
1回チェックしてから書き込むので普通ですから、物凄くピーキーな処理以外ではあまり気にしない方が良いと思います。物理的に書き込む時間とインメモリをチェックする時間には大きな開きがありますから。
fopenとか生書きで直後にobjectを走査するコードがあるとしたら、走査回数を気にするよりよほど問題だと思います。
質問の要件で、僕の場合のC++における理想のコードは以下のような形です。
(以下Embarcadero社製のC++Builderを使っているコードなので違和感があるかもしれませんが)
```
void WriteObjectsToJson(String path, const std::vector<Object> &objects)
{
   auto result = Validator::Validate(objects);
   if (!result.Success) return;
   std::unique_ptr<Stream> stream(new TFileStream(path, fsCreate));
   JsonSerializer::Serialize(objects, stream);
}
```
この際のValidationするクラスや、シリアライズするクラスは適当です。
クラスじゃなくてメソッドでもいい。ただこういう呼び出しの組み合わせで機能を実現する、ということです。
ここで説明したいのは、この時、「2回全体の走査が行われる」とかを一切気にしないということです。
されるのが当たり前だと考えているのもあるし、移譲された処理の先で何が起きているか考えること自体が少し違うかなと考えているからです。
それに上記の処理には再利用性があります。
例えば、TCP:IP通信でJson返したいんだよね、とか、WebのレスポンスにJson入れたいんだよね、という時には、ファイルストリームではなく、そちらも大体Stream関連のクラスが用意されているもので、わずかな手間で結果の出力先を変えることができます。
Validation関連の処理は書き込み前にも使えますが、フロントエンドでUI上で簡易なエラー検査処理を行う際にも使えます。(サービスとクライアントが分かれている場合、検査が2回行われるのはありえる。)  
fopenの前にやってるobjectsの走査処理と、書き込み操作処理を合わせるというのは、この完全に分割できる二つの処理を、なんとか混ぜあわせて、処理回数を削ろうとしているのに等しいです。
ストリームに一旦書き込めば1個のループに書き込みつつ、ファイルに書き込まないということはできます。しかし、それはつまり書き込む前に一旦メモリに全部書き出すということで今以上にリソースの観点からすればスマートではないやり方になると思います。ストリームがファイルストリームなら結局ファイルができてしまいますし。
ストリームにする最大の利点は、どのストリームに書き込むかを選択できることですからね。
現在言えることは、検査と書き込みを合体することはオススメできません。
いやいや、ものすごくピーキーなんだよ!データ数も物凄い量なんだよ!この処理は!
という時は、異常なデータが作られるのはどこなのか、どこでその間違いを防げるのか、そういうところを探し、そこで検査を行って、書き込み処理に来るデータを正常なものだけになるようにしましょう。
2 先にまとめを追加

haru666

haru666 score 1519

2017/03/01 11:29  投稿

現状の作りで正しいと思います。
取りうる選択肢は以下の2つだけです。  
・現状の形を維持する  
・正常なデータのみが書き込み処理に到達するようにする  
1回チェックしてから書き込むので普通ですから、物凄くピーキーな処理以外ではあまり気にしない方が良いと思います。物理的に書き込む時間とインメモリをチェックする時間には大きな開きがありますから。
fopenとか生書きで直後にobjectを走査するコードがあるとしたら、走査回数を気にするよりよほど問題だと思います。
質問の要件で、僕の場合のC++における理想のコードは以下のような形です。
(以下Embarcadero社製のC++Builderを使っているコードなので違和感があるかもしれませんが)
```
void WriteObjectsToJson(String path, const std::vector<Object> &objects)
{
   auto result = Validator::Validate(objects);
   if (!result.Success) return;
   std::unique_ptr<Stream> stream(new TFileStream(path, fsCreate));
   JsonSerializer::Serialize(objects, stream);
}
```
この際のValidationするクラスや、シリアライズするクラスは適当です。
クラスじゃなくてメソッドでもいい。ただこういう呼び出しの組み合わせで機能を実現する、ということです。
ここで説明したいのは、この時、「2回全体の走査が行われる」とかを一切気にしないということです。
されるのが当たり前だと考えているのもあるし、移譲された処理の先で何が起きているか考えること自体が少し違うかなと考えているからです。
それに上記の処理には再利用性があります。
例えば、TCP:IP通信でJson返したいんだよね、とか、WebのレスポンスにJson入れたいんだよね、という時には、ファイルストリームではなく、そちらも大体Stream関連のクラスが用意されているもので、わずかな手間で結果の出力先を変えることができます。
fopenの前にやってるobjectsの走査処理と、書き込み操作処理を合わせるというのは、この完全に分割できる二つの処理を、なんとか混ぜあわせて、処理回数を削ろうとしているのに等しいです。
ストリームに一旦書き込めば1個のループに書き込みつつ、ファイルに書き込まないということはできます。しかし、それはつまり書き込む前に一旦メモリに全部書き出すということで今以上にリソースの観点からすればスマートではないやり方になると思います。ストリームがファイルストリームなら結局ファイルができてしまいますし。
ストリームにする最大の利点は、どのストリームに書き込むかを選択できることですからね。
現在言えることは、検査と書き込みを合体することはオススメできません。
いやいや、ものすごくピーキーなんだよ!データ数も物凄い量なんだよ!この処理は!
という時は、異常なデータが作られるのはどこなのか、どこでその間違いを防げるのか、そういうところを探し、そこで検査を行って、書き込み処理に来るデータを正常なものだけになるようにしましょう。
1 会社名の誤りの修正

haru666

haru666 score 1519

2017/03/01 11:26  投稿

現状の作りで正しいと思います。
1回チェックしてから書き込むので普通ですから、物凄くピーキーな処理以外ではあまり気にしない方が良いと思います。物理的に書き込む時間とインメモリをチェックする時間には大きな開きがありますから。
fopenとか生書きで直後にobjectを走査するコードがあるとしたら、走査回数を気にするよりよほど問題だと思います。
質問の要件で、僕の場合のC++における理想のコードは以下のような形です。
(以下Delphi社製のC++Builderを使っているコードなので違和感があるかもしれませんが)
(以下Embarcadero社製のC++Builderを使っているコードなので違和感があるかもしれませんが)
```
void WriteObjectsToJson(String path, const std::vector<Object> &objects)
{
   auto result = Validator::Validate(objects);
   if (!result.Success) return;
   std::unique_ptr<Stream> stream(new TFileStream(path, fsCreate));
   JsonSerializer::Serialize(objects, stream);
}
```
この際のValidationするクラスや、シリアライズするクラスは適当です。
クラスじゃなくてメソッドでもいい。ただこういう呼び出しの組み合わせで機能を実現する、ということです。
ここで説明したいのは、この時、「2回全体の走査が行われる」とかを一切気にしないということです。
されるのが当たり前だと考えているのもあるし、移譲された処理の先で何が起きているか考えること自体が少し違うかなと考えているからです。
それに上記の処理には再利用性があります。
例えば、TCP:IP通信でJson返したいんだよね、とか、WebのレスポンスにJson入れたいんだよね、という時には、ファイルストリームではなく、そちらも大体Stream関連のクラスが用意されているもので、わずかな手間で結果の出力先を変えることができます。
fopenの前にやってるobjectsの走査処理と、書き込み操作処理を合わせるというのは、この完全に分割できる二つの処理を、なんとか混ぜあわせて、処理回数を削ろうとしているのに等しいです。
ストリームに一旦書き込めば1個のループに書き込みつつ、ファイルに書き込まないということはできます。しかし、それはつまり書き込む前に一旦メモリに全部書き出すということで今以上にリソースの観点からすればスマートではないやり方になると思います。ストリームがファイルストリームなら結局ファイルができてしまいますし。
ストリームにする最大の利点は、どのストリームに書き込むかを選択できることですからね。
現在言えることは、検査と書き込みを合体することはオススメできません。
いやいや、ものすごくピーキーなんだよ!データ数も物凄い量なんだよ!この処理は!
という時は、異常なデータが作られるのはどこなのか、どこでその間違いを防げるのか、そういうところを探し、そこで検査を行って、書き込み処理に来るデータを正常なものだけになるようにしましょう。

思考するエンジニアのためのQ&Aサイト「teratail」について詳しく知る