D構造体
値オブジェクトのパターンは、構造体とその組み込みの値セマンティクスを使用するだけで、Dで最もよく表されます。
私の理解では、値オブジェクトパターンは通常Javaで使用されます。これは、Javaには現在、値セマンティクスを備えた組み込みの集計がないためです。
Dの構造体は、CとC#の構造体、およびC++の構造体とクラスと同様に機能します。D構造体にはコンストラクタとデストラクタがあるため、比較はおそらく後者に最適ですが、1つの重要な例外があります。継承と仮想関数はありません。これらの機能は、JavaやC#のクラスとほとんど同じように機能するクラスに委任されます(これらは暗黙の参照型であるため、スライスの問題が発生することはありません)。
struct Rational
{
int num;
int den;
/* your methods here */
}
その後、Rationalのインスタンスは常に値によって渡され(パラメーターで明示的に指定されていない限り、refおよびoutを参照)、割り当て時にコピーされます。
純度
純粋関数は、グローバル状態に対して読み取りまたは書き込みを行うことはできません。this
純粋関数は、メソッドの暗黙的なパラメーターだけでなく、明示的なパラメーターも変更できます。したがって、Rationalのメソッドはおそらく常にpure
です。
std.string.format
存在しないことpure
は、現在の実装の問題です。将来的には別の実装を使用しますpure
。
定数と不変
メソッドが純粋であり、それ自体の状態を変更しないことを表現したい場合は、との両方にすることができpure
ますconst
。
可変( )インスタンスRational
と不変(immutable(Rational)
)インスタンスはどちらも暗黙的にに変換できるconst(Rational)
ためconst
、不変の保証は必要ないが、メンバーを変更しない場合に最適です。
一般に、メンバーフィールドを変更する必要のないstructメソッドは。である必要がありますconst
。クラスの場合も同じことが当てはまりますが、メソッドをオーバーライドする可能性のある派生メソッドについても考慮する必要があります。これらは同じ制限に拘束されます。
orを宣言することはconst
、そのすべてのメンバー(メソッドを含む)またはをそれぞれマークすることと同じです。immutable
struct
class
const
immutable
不変コンストラクター
num
コンストラクターが行うのがフィールドとden
フィールドをそれぞれのコンストラクターパラメーターに割り当てることだけである場合、この機能はデフォルトで構造体にすでに存在します。
struct S { int foo, bar; }
auto s = S(1, 2);
assert(s.foo == 1);
assert(s.bar == 2);
const
すべてが暗黙的にconstに変換可能であるため、コンストラクターでは、不変性に関係なくどのコンストラクターでもconstインスタンスを構築できるため、あまり意味がありません。
immutable
コンストラクターでは意味があり、構造体またはクラスの不変インスタンスを構築する唯一の方法である場合があります。this
可変コンストラクターは、インスタンスを後で変更できる参照のエイリアスを作成する可能性があるため、その結果が常に暗黙的に不変に変換されるとは限りません。
ただし、Rationalには間接参照がないため、不変のコンストラクターは必要ありません。したがって、可変のコンストラクターを使用して、結果をコピーすることができます。言い換えると、可変間接参照のない型は、暗黙的に不変に変換可能です。int
これには、とのようなプリミティブ型とfloat
、同じ条件を満たす構造体が含まれます。
効果のない属性
効果がない宣言に付けられた属性は、現在のすべてのコンパイラーによって無視されます。attribute { /* declarations */ }
とattribute: /*declarations*/
構文を使用して、属性を一度に複数の宣言に適用できるため、これは理にかなっています。
struct S
{
immutable
{
int foo;
int bar;
}
}
struct S2
{
immutable:
int foo;
int bar;
}
上記の両方の例で、foo
およびbar
はタイプimmutable(int)
です。
クラスの使用
大きな構造体の頻繁なコピーに関連するパフォーマンス上の理由など、値のセマンティクスが望ましくない場合があります。ref
関数パラメーターの使用やポインターの使用など、参照によって構造体を明示的に渡すことは可能out
ですが、値のセマンティクスがデフォルトの場合、間違いを犯しやすく、構文上のオーバーヘッドが大きくなる可能性があります。ポインタには、他にも多くの落とし穴があります。
クラスは参照型であり、値のように扱うことはできません。これらは通常、でインスタンス化されますnew
。これは、常にクラスのGC割り当てインスタンスを作成します(のオーバーロードnew
は非推奨です)。これらの2つのポイントにより、DのクラスはJavaおよびC#のクラスと非常によく似ています(もう1つの注目すべき点は、多重継承の代わりにインターフェイスがあることです)。ただし、クラスには非表示フィールドのオーバーヘッド(現在size_t.sizeof * 2
はすべてのクラスのバイト)があり、フィールドのABIは指定されていませんが、継承と仮想関数が必要な場合は、クラスも唯一のオプションです。
値オブジェクトパターンに実装されたRationalは次のとおりです。
class Rational
{
immutable int num;
immutable int den;
this(int num, int den)
{
this.num = num;
this.den = den;
}
/* methods here */
}
これは、Java実装に最も忠実な実装です。インスタンス自体の変更可能性に関係なく、変更num
を防ぐために不変を使用します。den
メソッドはconst
、通常pure
、構造体と同じである必要があります。
不変コンストラクターは現在完全には実装されていないため(読み取り:まったく使用しないでください)、コンストラクターが参照new immutable(Rational)(1, 2)
の可変エイリアスを自由に作成できる場合でも、上記のコンストラクターを使用すると、クラスの不変インスタンスを実際に作成できます(例)。this
、不変の保証を破る。
もう少しDに似た方法は、不変性の決定をユーザーコードに任せて、次のようにわかりやすく実装することです。
class Rational
{
int num;
int den;
this(int num, int den)
{
this.num = num;
this.den = den;
}
/* immutable constructor overload would be here */
/* methods here */
}
その後、ユーザーはを使用するか、を使用するかを選択できRational
ますimmutable(Rational)
。後者は、std.concurrencyスレッドインターフェイスを使用してスレッド間で安全に渡すことができますが、前者を送信しようとすると、コンパイル時に拒否されます。
ただし、後者には明白な問題があります。これRational
は暗黙的に参照型であるため、Rationalの不変インスタンスへの可変参照を入力する方法はありません。この問題の現在の解決策は、std.typecons.Rebindableを使用することです。言語でこれを修正するための提案された解決策があります。