5

Customerいくつかのプロパティ ( IDFirstName、 ) を持つオブジェクトがあるとしますLastName。私はデフォルトのコンストラクターを持っていますCustomer()Customer(DataRow dr)、データベースからこのオブジェクトをロードするのは簡単な方法であるため、 も持っています。

Customer(int ID)をロードしたいがCustomer、まだデータベースにアクセスしていない場合に備えて、別のコンストラクター をセットアップしたくなることがよくあります。私にとって最も簡単な方法は次のようです。

Customer(int ID)
{
    DataTable dt = DataAccess.GetCustomer(ID);
    if (dt.Rows.Count > 0)
    {
        // pass control to the DataRow constructor at this point?
    }
    else
    {
        // pass control to the default constructor at this point?
    }   
}

DataRow コンストラクターに既にあるコードを再利用することは理にかなっていますが、それを呼び出して、それが提供するものを返す方法がわかりません。グーグルを通じて、: this()構文でのコンストラクターのオーバーロードに関する情報を見つけましたが、これらの例はすべて、私がやろうとしていることと後方または互換性がないようです。

そのため、コンストラクターの理解にはギャップがありますが、整理できないようです。私は何が欠けていますか?

4

6 に答える 6

11

最も簡単な解決策は、やりたい仕事をする別の関数を構築し、両方のコンストラクターにその関数を呼び出させることです。

于 2008-12-20T14:06:13.700 に答える
6

あなたが得られないのはコンストラクターではなく、単一責任の原則と疎結合に関するものではないかと心配しています。

たとえば、上記のコードは次のことを意味します。

  • ドメイン モデルにデータ アクセス コードが含まれています
  • そもそもデータアクセスロジックで注入される可能性のある基本クラスのコードを再利用していない
  • ドメイン オブジェクトは、DataTable や DataRow など、それ自体またはそのメンバー以外のデータ構造を認識しているため、それらのデータ構造と結びついており、他のデータ構造を使用するのが面倒です。

もちろん、これは ActiveRecord モデルを使用していないことを前提としています。ここではそのように見えますが、それでも密結合で実装されます。

私の好みは、ドメイン オブジェクトには、本物の顧客データの保持と操作に関連するロジックのみが含まれ、それ以外は何も含まれないことです。そのため、私のコンストラクターは次のようになります。

class Customer
{
    public Customer(int id, string firstName, string LastName)
    {
        Id = id;
        FirstName = firstName;
        LastName = lastName;
    }

    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

更新:そうは言っても、これは、一部の人々が NHibernate のような POCO を可能にする ORM を好む主な理由です: そこにデータ読み込みロジックを配置する必要はありません。

たとえば、これが NHibernate で行われた場合、DomainObject 基本クラスが必要になります。

public class Customer : DomainObject

これは、NHibernate の IRepository の実装によって消費される可能性があります。

public class Repository<T> : IRepository where T : DomainObject

このRepositoryオブジェクトには、CRUD 操作に必要なすべてのコードが含まれます。

ADO.NET を使い続けたい場合、考えられる解決策の 1 つは、すべての読み込みに対して DAL マネージャー オブジェクトを作成することです。

public class CustomerManager
{
    public IList<Customer> LoadCustomers()
    {
        //load all customers here
        foreach (DataRow dr in dt.Table[0])
        {
             yield return new Customer((int) dr["Id"], dr["FirstName"].ToString(), dr["LastName"].ToString());
        }
    }

    public Customer LoadCustomerByID(int id)
    {
        //load one customer here
        return new Customer((int) dr["Id"], dr["FirstName"].ToString(), dr["LastName"].ToString());
    }
}

もちろん、ここでコードの再利用をさらに促進する機会は他にもたくさんあります。

于 2008-12-20T14:43:52.347 に答える
3

引数に基づいてまったく異なることを行うさまざまなコンストラクターが多数あると、多くの場合、コードが読みにくくなります。これを行うためのより良い方法は、クラスの名前を明らかにする目的で多数の静的作成メソッドを作成することです。次に、コンストラクターは 1 つだけです。必要に応じて、すべてのコンストラクターを非公開にすることもできます。クライアントは、静的メソッドを使用してクラスのインスタンスを作成します。

したがって、代わりに:

Customer c = new Customer(13, "George", "Bush");
Customer c2 = new Customer(12);
Customer c3 = new Customer(GetDataRow(11));

あなたは得る:

Customer c = new Customer(13, "George", "Bush");
Customer c2 = Customer.LoadFromDatabaseId(12);
Customer c3 = Customer.MapFromDataRow(GetDataRow(11));

顧客クラスは次のようになります。

class Customer
{
    public Customer(int id, string firstName, string lastName)
    {
        //...
    }

    static public Customer MapFromDataRow(DataRow dr)
    {
        return new Customer(
            dr["ID"],
            dr["FirstName"],
            dr["LastName"]);
    }

    static public Customer LoadFromDatabaseId(int id)
    {
        DataTable dt = DataAccess.GetCustomer(ID);
        if (dt.Rows.Count > 0)    
        {
            return MapFromDataRow(dt.Rows[0]);
        }
        else    
        {        
            throw new CustomerNotFoundException(id);                
        } 
    }
}
于 2008-12-20T15:24:47.147 に答える
1

このコンストラクタ構文を使用するだけです

 public Customer(int ID): this(DataAccess.GetCustomer(ID).Rows[0]) {}

ただし、この構造に無効なID(データベースにないID)を渡すと、例外がスローされます。

于 2008-12-20T16:53:54.563 に答える
1

コンストラクターを少し変更すると、これを希望どおりに機能させることができます...しかし、いいえ、コンストラクターの本体内から別のコンストラクターを呼び出す方法はありません。しかし、ここでできることは次のとおりです。

DataRows を取るコンストラクターを DataTable を取るように変更し、最初に既定のコンストラクターを呼び出します。

Customer( DataTable dt ) : Customer()
{
    if ( dt != null && dt.Rows.Count > 0 )
    {
        // handle the row that was selected
    }
    else
    {
        throw Exception( "customer not in database" ); // or leave this line out to allow a default customer when they arent in the DB
    }
}

次に、ID コンストラクターを次のように変更します。

Customer(int ID) : Customer(DataAccess.GetCustomer(ID))
{
    // no code
}

したがって、デフォルトのコンストラクターが常に呼び出されるようになり、DB で顧客が見つかった場合は、デフォルト値をデータベースの値で上書きできます。顧客が DB に存在しない場合は、例外をスローするか、デフォルト値のみで顧客を構築できるようにすることができます。

于 2008-12-20T14:30:04.090 に答える
0

この新しいメソッドで、取得したデータ行から Customer を構築するか、初期化されていない Customer を構築してから手元のデータからデータ (ID など) の設定を開始するかを選択できるようにする場合は、代わりにファクトリを使用することをお勧めします。別のコンストラクターの。簡単な疑似コードのようなスケッチは次のようになります。

Customer ProvideCustomer(int ID)
{
    Customer result; // or initialize to null to signal more work to come
    DataTable dt = DataAccess.GetCustomer(ID);
    if (dt.Rows.Count > 0)
    {
        result = new Customer( dt.getappropriaterow ) // however you choose one
    }
    else
    {
        result = new Customer();
        result.ID = ID;          // whatever other initialization you need
    }
    return result;
}
于 2008-12-20T14:24:58.267 に答える