7

私は常に特定の値を持たなければならないオブジェクトを書いています。最も注目すべきは、常にNameプロパティの値が必要です。

public class User
{
    public string Name { get; set; }

    public User(string name)
    {
        Name = name;
    }
}

さて、このクラスで実装する必要のあるビジネスルールがいくつかあります。その1つは、Nameプロパティは一意の名前でなければならないということです。したがって、このオブジェクトの初期化子は次のようになります。

    public User(string name, IQueryable<User> allUsers)
    {
        var matches = allUsers.Where(q => q.Name == name).ToList();
        if(matches.Any())
        {
            // abort object initialization
        }
        Name = name;
    }

しかし、オブジェクトの初期化をどのように中止するかはわかりません。実際、それも可能ですか?

オブジェクトの初期化を中止する方法はありますか(つまり、オブジェクトをnullに設定する)、またはこれを実行するためのより良い方法はありますか?

4

9 に答える 9

6

まあ、あなたはただ例外を投げるでしょう。しかし、私はこの問題を処理するこの方法がまったく好きではありません。むしろ、サービスを介してユーザーを作成し、名前が有効かどうかをサービスに確認させる必要があります。

于 2012-05-08T14:57:59.940 に答える
4

オブジェクトのコンストラクターまたは名前セッターで例外をチェックしてスローできると思いますが、これには多くの問題とさまざまな懸念が伴う可能性があります。私は、このチェックを行い、null(または適切な名前の例外)を返すファクトリを介してオブジェクトを作成すると言います。または、POCOオブジェクトを作成し、別のクラス/メソッドを介して検証を行います。

于 2012-05-08T14:57:17.460 に答える
3

パブリックコンストラクターを使用する代わりに、次のようなメソッドとプライベートコンストラクターを使用します。

public static User CreateUser(string name)
{
      // Check whether user already exists, if so, throw exception / return null

      // If name didn't exist, record that this user name is now taken.
      // Construct and return the user object
      return new User(name);
}

private User(string name)
{
       this.Name = name;
}

次に、呼び出し元のコードはUser myUser = User.CreateUser("Steve");、それに応じてnullリターン/例外を使用および処理できます。

使用しているメソッドがどのユーザー名を取得するかを格納する場合は、この名前がCreateUserメソッド内で取得されるように更新する必要があることを追加する価値があります。そうしないと、このオブジェクトをデータベースなどに保存する前にしばらく待つと、問題が発生します。これを明確にするために、上記のコードを更新しました。

于 2012-05-08T15:02:34.620 に答える
3

オブジェクトの初期化を中止するには、コンストラクターで例外をスローします。無効な入力を拒否することをお勧めします。

public class User
{
    public User(String name) {
        if (String.IsNullOrWhiteSpace(name)) {
            if (name == null) {
                throw new System.ArgumentNullException("Cannot be null.", "name");
            }
            else {
                throw new System.ArgumentException("Cannot be empty.", "name");
            }
        }
    }
}

コンストラクターで定義したいビジネスロジックがそこに適合しません。コンストラクターは軽量で、インスタンス化のみである必要があります。一部のデータソースのクエリは、コンストラクターにとってコストがかかりすぎます。このため、代わりにファクトリパターンを使用する必要があります。ファクトリパターンを使用すると、呼び出し元は、オブジェクトの作成に伴う手間のかかる作業が発生することを期待する場合があります。

public class User
{
    private User(String name) {
        if (String.IsNullOrWhiteSpace(name)) {
            if (name == null) {
                throw new System.ArgumentNullException("Cannot be null.", "name");
            }
            else {
                throw new System.ArgumentException("Cannot be empty.", "name");
            }
        }
    }

    public static User CreateUser(String name) {
        User user = new User(name); // Lightweight instantiation, basic validation

        var matches = allUsers.Where(q => q.Name == name).ToList();

        if(matches.Any())           
        {           
            throw new System.ArgumentException("User with the specified name already exists.", "name");         
        }     

        Name = name;
    }

    public String Name {
        get;
        private set; // Optionally public if needed
    }
}

ファクトリパターンの方が適していることがわかります。これはメソッドであるため、呼び出し元は、ファクトリパターンを呼び出すことで何らかの作業が行われていることを期待できます。一方、コンストラクターを使用すると、軽量であることが期待されます。

コンストラクタールートを使用する場合は、データソースへの実際の挿入が試行されるときなど、ビジネスルールを適用する他の方法を試してください。

public class User
{
    public User(String name) {
        if (String.IsNullOrWhiteSpace(name)) {
            if (name == null) {
                throw new System.ArgumentNullException("Cannot be null.", "name");
            }
            else {
                throw new System.ArgumentException("Cannot be empty.", "name");
            }
        }
    }
}

public class SomeDataSource {
    public void AddUser(User user) {
        // Do your business validation here, and either throw or possibly return a value
        // If business rules pass, then add the user
        Users.Add(user);
    }
}
于 2012-05-08T15:11:53.667 に答える
2

ユーザーを作成する前に、重複する名前を確認する必要があります。

于 2012-05-08T14:57:54.480 に答える
2

個人的には、インスタンス化する前にロジックチェックを実行します。例えば:

if(UserLogic.PreInsertValidation(string username)){
   User newUser = new User(username);
}
else{
  // Handling - maybe show message on client "The username is already in use."
}

PreInsertValidation要件に基づいて、すべてのビジネスロジックチェックが行われます。

于 2012-05-08T15:01:00.283 に答える
0

あなたが探しているのは、アイデンティティマップパターン、またはこれの一種です。この責任をオブジェクト自体に任せるのはおそらく間違っています。エンティティを作成するコンポーネントで行う必要があります。もちろん、競合状態を回避するために、必要に応じてマップはスレッドセーフである必要があります。

于 2012-05-08T15:02:13.577 に答える
0

これは、ユーザーをコレクションに含めるというコミットで処理します。たとえば、ユーザーのコレクションを編集してからデータベースに永続化する場合、永続化レイヤーが検証を担当します。私はいつも、他のすべてのオブジェクトを同じように維持する責任をオブジェクトに持たせることは悪い習慣だと考えてきました。オブジェクトがない場合は、オブジェクト自体との親子関係を導入します。これを処理するために、ある種の検証エンジンを実装することをお勧めします。

于 2012-05-08T15:02:30.230 に答える
0

オブジェクト自体の内部でこの検証を行う代わりに、このエンティティの作成、検証、および保存をサービスに配置します。このサービスは、ユーザー名が一意でない場合にをスローするValidationException可能性があり、競合状態が発生しないようにするためにトランザクションを開始することもできます。私が使用する良いモデルは、コマンド/ハンドラーパターンです。次に例を示します。

public class CreateNewUserCommand
{
    public string UserName { get; set; }
}

internal class CreateNewUserCommandHandler
    : ICommandHandler<CreateNewUserCommand>
{
    private readonly IUnitOfWork uow;

    public CreateNewUserCommandHandler(
        IUnitOfWork uow)
    {
        this.uow = uow;
    }

    public void Handle(CreateNewUserCommand command)
    {
        // TODO Validation

        var user = new User { Name = command.Name };

        this.uow.Users.InsertOnSubmit(user);
    }
}

検証を独自のクラスに追加することもできます。

于 2012-05-08T15:08:49.623 に答える