Xmlクラスはどのような役割を持つべきなのか?
Xmlクラスは、XmlSelializerを使用したときに、XMLファイルと相互変換するオブジェクトのクラスです。
getter、setter プロパティだけだとドメイン貧血症を引き起こします。
だからと言って、型変換やnull時は例外発生させる処理を実装するのは仕事をやりすぎにも思います。
今回は、C# におけるXmlクラスの役割について考えてみました。
- Windows10
- Microsoft Visual Studio Community2022
- .NET 6
- WPF アプリ
Xmlクラスとは
まずは、Xmlクラスとは何かという話です。Xmlクラスは、ぼくが勝手に定義したものです。改めて、Xmlクラスを以下のように定義します。
Xmlクラスは、XmlSelializerを使用したときに、XMLファイルと相互変換するオブジェクトのクラスです。
↓の図だと、AdministratorXmlクラスが該当します。
XmlSerilaizerの使い方に関しては、↓の記事を参考にしてください。
Xmlクラスの方針
ぼくは、Xmlクラスは、XMLファイルとの相互変換に特化したクラスと考えています。具体的には以下の2つの方針です。
- XMLファイルの内容をそのまま表す
- 相互変換以外はしない
XMLファイルの内容をそのまま表す
Xmlクラスを使用するクライアントが、XMLファイルの内容をそのまま使えるようなものにします。
- タグの要素の内容とメンバ変数の値が一致している
- タグが存在しない場合は、存在しないことを表す。
これだけで、1つの役割を持つクラスと言えるでしょう。
相互変換以外はしない
相互変換以外に余計なことはしないようにします。
- 読み込んだ文字列→ 数値やbool型に変換
- 定義した型がない場合は例外を発生させる
この処理はXMLファイルとの相互変換とは関係ありません。Xmlクラスを使用する側の役割としたほうがよいでしょう。
Xmlクラスのルール
前述した方針に基づいて、Xmlクラスのルールを決めます。
まずは、XmlSerializerを使用するときのXmlクラスの制約です。
- public クラスとする
- メンバ変数はpublicとする
- publicで引数のないコンストラクタを実装する
追加で以下のルールを決めます。
- 値型はすべてstring型とする
- すべてのメンバ変数はnull許容型とする
- メソッドは使用しない
ルールを適用したXmlクラスは以下のようになります。
namespace XmlProject
{
/// <summary>ユーザー一覧Xmlクラス</summary>
/// <remarks>publicクラスとする</remarks>
public class UsersXml
{
/// <summary>ユーザー一覧</summary>
/// <remarks>null許容型</remarks>
public List<UserXml>? userList;
/// <summary>コンストラクタ</summary>
/// <remarks>
/// 空のコンストラクタは必須
/// XmlSelializerが使用する
/// </remarks>
public UsersXml(){}
/// <summary>コンストラクタ</summary>
/// <param name="userList">ユーザー一覧</param>
/// <remarks>
/// 値を設定するコンストラクタ
/// クライアントはこれを使用する
/// </remarks>
public UsersXml( List<UserXml>? userList )
{
this.userList = userList;
}
}
}
namespace XmlProject
{
/// <summary>管理者Xml</summary>
/// <remarks>publicクラスとする</remarks>
public class AdministratorXml : UserXml
{
/// <summary>ID</summary>
/// <summary>null許容型とする</summary>
public string? id;
/// <summary>パスワード</summary>
public string? pass;
/// <summary>年齢</summary>
/// <summary>int型でなくstring型</summary>
public string? age;
/// <summary>年齢表示判定</summary>
/// <summary>bool型でなくstring型</summary>
public string? isViewAge;
/// <summary>役職</summary>
public string? position;
/// <summary>コンストラクタ</summary>
public AdministratorXml(){}
/// <summary>
/// コンストラクタ
/// </summary>
public AdministratorXml(string? id, string? pass, string? age, string? isViewAge, string? position)
{
this.id = id;
this.pass = pass;
this.age = age;
this.isViewAge = isViewAge;
this.position = position;
}
}
}
値型はすべてstring型とする
XmlSerializerを使用する場合、int型、bool型なども指定できます。
が、あえてstring型とします。
XmlクラスはXMLファイルの相互変換だけが目的なので、XMLファイルに書かれているそのままのものとします。
int型、bool型の変換はクライアント側の処理です。
すべてのメンバ変数はnull許容型とする
Xmlクラスで定義されているメンバ変数対し、対応するタグがXMLファイルに存在しないかもしれません。
それを表すために、タグが存在していないこと = メンバ変数がnull として表します。
クライアントには、値がないことだけ伝えます。値がないときの処理はクライアントが行います。
メソッドは使用しない
- 値を取得するとき、型変換を行う
- 値を取得するとき、nullなら例外を発生させたい
こういったメソッドは使用しないようにします。XMLファイルとの相互変換が目的だからです。型変換、例外発生は、相互変換の範囲外です。
この処理はクライアント側で実装します。
Xmlクラスは、publicのメンバ変数が必須です。仮に、型変換メソッドを作ったとしても、メンバ変数が取得できてしまいます。
それなら、クライアント側にXmlクラスを変更するための専用クラスを作ったほうがいいです。
まとめ
Xmlクラスの役割について書きました。
Xmlクラスは、XMLファイルとの相互変換に特化したクラスとしました。
それ以外に余計なことはしないようにします。
コメント