-4

オブジェクト指向言語でクラスやオブジェクトについて話すときに、現実世界と比較する方法について考えていました。人々が継承について話すとき、人々は親と子の例を引用するように. 私が知っている OO 言語、主に C、C++、C# で見つからないことの 1 つは、プロパティを必須として宣言するメカニズムがないことです。つまり、人間と呼ばれるクラスを定義して、顔、手、目がクラスの必須プロパティであると言うことができないということです。その構造を持つことで、私のクラスを使用しているすべての人が、私のクラスを使用する前にそれらのプロパティを設定する必要があることを強制できます。ユーザーがこれらのプロパティを設定するのを忘れた場合、コンパイル時エラーが発生するはずです。

それについてのコミュニティの考えを見たかっただけです。

上記の質問をした理由は次のとおりです。

ユーザー コントロールを作成するとき、ユーザーがコントロールを使用するときに、コード内のいくつかのプロパティを設定する必要があることを確認したいと考えています。たとえば、チーム内の他の開発者が使用する顧客ユーザー コントロールを作成するとします。私が公開したプロパティには、「CustomerId」、「FirstName」、「LastName」、「Address1」、「City」、「State」、および ZipCode があります。ここで、コントロールのコンシューマーが「CustomerId」を設定する必要があることを確認したいと思います。コンストラクターを使用して値が設定されていることを強制する方法がありますが、実行時例外がスローされ、ユーザーがコントロールを動的に作成してコントロール コレクションに追加せずに .cs ファイルからそのコンストラクターを呼び出す方法があります。

4

4 に答える 4

2

その理由は、クラスの不変条件を満たすために必要な状態をオブジェクトの構築中に提供する必要があるためです。そのため、「必須」プロパティの値をコンストラクター パラメーターとして提供する必要があります。あなたの質問は、オブジェクトがプロパティで状態を設定することによって特徴付けられるという誤った仮定に基づいています。これはいくつかの理由で間違っています。そのいくつかは次のとおりです。

  • ほとんどではないにしても、多くの OO 言語にはプロパティがありません: Java、C++、...
  • 使用するものは形式的にのみオブジェクトであり、実際には単純なレコードであり、オブジェクト指向ではありません。たとえば、メソッドのない C++ 構造体と同じです (セッターとメソッドについては、下部のメモを参照してください)。

クライアントがオブジェクトのインスタンスを作成できるようにすることは、後で必須状態の正しい値を設定するだけで、デバッガーと一緒に何時間も費やすことになります。

User姓と名を常に設定する必要があるという不変条件をいくつか考えてみましょう。

class User {
    public User(string first, string last) { ... }
    public User(string first, string last, uint age) : this(first, last) { ... }
}

// client code:
var user = new User("john", "doe");
var user2 = new User("Clint", "Eastwood", 82);

コンパイラは、不変条件を満たさない限り、誰もオブジェクトをインスタンス化できないことを保証します。

それをあなたのアプローチと比較してください:

class User {
    public User(string first, string last) { ... }
    public User(uint age)  { ... }
    [Mandatory] public string FirstName { get; set; }
    [Mandatory] public string LastName { get; set; }
}

// client code:
var actor = new User(82); // << invalid
actor.FirstName = "Clint";
actor.LastName = "Eastwood"; // << valid

このアプローチにより、より多くのコードが生成され、オブジェクトが有効な状態にない期間 ( ~ の間) が許容され<< invalidます。<< valid一部のプロパティ セッターが例外をスローした場合はどうなりますか? 壊れたオブジェクト インスタンスが浮かんでいます。セッターのコードがスローできないこともコンパイラーが検証すると思いますか? それは可能だと思いますか?それに加えて、インスタンスをインスタンス化するすべてのクライアントUserは、必須プロパティとは何かを確認し、それらすべてを確実に設定する必要があります。これにより、カプセル化が効果的に解除されます。

IMO、プロパティ セッターは、ゲッターとは異なり、まれである必要があります。このようなクラスでは、FirstName/LastName のセッターは使用せず、getter のみを使用する必要があると思います。SetName(string first, string last)名前の変更を本当に許可したい場合は、代わりにメソッドが必要です。理由は次のとおりです。

// lets rename actor
actor.FirstName = "John";
actor.LastName = "Wayne"; 

最後の行がスローされた場合、私が聞いたことのない俳優のジョン・イーストウッドが残ります。これactor.SetName("John", "Wayne")ではありえない。

さらに、指定する順序で依存関係を持つプロパティについてはどうでしょうか。

obj.ErrorCode = 123;  // imagine that error code must be != 0
obj.ErrorMsg = "foo"; // in order to be allowed to set error code

持つ代わりに、そのための属性も導入しますobj.SetErrorInfo(123, "foo")か? これにより、メソッド呼び出しとは異なり、順序が実装の詳細によって引き起こされるため、プロパティがカプセル化を破ることが明らかになります。

多くの場合、C# などの言語では、必要な状態または依存関係はコンストラクターで提供されますが、オプションの状態はプロパティを介して設定できます。ただし、言語をオブジェクト指向にするのはプロパティや継承ではありません。

于 2013-03-12T19:37:13.187 に答える
2

これは、DDD の原則に従って実行できます。つまり、プライベート デフォルト コンストラクターと、必要なパラメーターを受け入れてその値を検証するパブリック コンストラクターを使用してクラスを作成します。値が無効な場合は、オブジェクトを作成できないように例外をスローします。プロパティは、パブリック セッターの代わりにプライベート セッターを持つこともできます。

「必須」属性を作成して、必須のプロパティの上に配置することもできます。プロパティが属性で装飾されているかどうかに基づいてこれをチェックするメカニズムがあります。

例:

public class BlogEntry 
{
    private BlogEntry() {}
    public BlogEntry(string title, string body)
    {
        LastModifiedDate = DateTime.Now;
        Title = title;
        Body = body;

        var blogEntryValidator = new BlogEntryValidator();
        blogEntryValidator.ValidateAndThrow(this);     
    }

    public int Id { get; private set; }
    public string Title { get; private set; }
    public string Body { get; private set; }
    public DateTime? LastPublishDate { get; private set; }
    public DateTime LastModifiedDate { get; private set; }
    public virtual ICollection<Comment> Comments { get; private set; }        

    public void Publish()
    {
        LastPublishDate = DateTime.Now;
    }

    public void Unpublish()
    {
        LastPublishDate = null;
    }

    public void Modify(string title, string body)
    {
        Title = title;
        Body = body;
        LastModifiedDate = DateTime.Now;
    }

    public Comment AddComment(string commentText, string emailAddress, string name)
    {
        var comment = new Comment(this, commentText, emailAddress, name);
        if (Comments == null) Comments = new List<Comment>();
        Comments.Add(comment);
        return comment;
    }

    public void RemoveComment(Comment comment)
    {
        Comments.Remove(comment);
    }
}

public class Comment 
{
    private Comment() {}
    public Comment(BlogEntry blogEntry, string name, string emailAddress, string commentText)
    {            
        BlogEntry = blogEntry;
        Name = name;
        EmailAddress = emailAddress;
        CommentText = commentText;
        DateWritten = DateTime.Now;

        var commentValidator = new CommentValidator();
        commentValidator.ValidateAndThrow(this);    
    }

    public int Id { get; private set; }        
    public string Name { get; private set; }
    public string EmailAddress { get; private set; }
    public string CommentText { get; private set; }
    public DateTime DateWritten { get; private set; }
    public BlogEntry BlogEntry { get; private set; }
}
于 2013-03-12T19:37:22.503 に答える
2

はい、C++ と C# では、コンストラクターを使用してこれを行うことができます。

class A
{
public: 
    A(int x, int y, int z)
      : _x(x_, _y(y), _z(z) {}
private:
    int _x;
    int _y;
    int _z;
};

、、およびAの値を指定せずにのインスタンスを作成することはできません。_x_y_z

于 2013-03-12T19:38:02.267 に答える
0

できますよ!コンストラクターでパラメーターを使用して、どれが必須かを示すだけです。

public class Human
{
    public Face Face { get; set; }
    public Hand Hand { get; set; }

    public Human(Face face, Hand hand) {} etc...
}

この例では、プライベート コンストラクターを使用できないため、これらのプロパティは、Human クラスを使用するために本質的に「必須」です。

于 2013-03-12T19:38:23.267 に答える