オブジェクトの適切なインスタンス化に失敗する可能性のあるコンストラクターの作成方法


11

場合によっては、失敗する可能性のあるコンストラクターを記述する必要があります。たとえば、次のようなファイルパスでオブジェクトをインスタンス化するとします。

obj = new Object("/home/user/foo_file")

パスが適切なファイルを指している限り、すべて問題ありません。ただし、文字列が有効なパスでない場合は、問題が発生するはずです。しかし、どのように?

あなたは出来る:

  1. 例外を投げる
  2. nullオブジェクトを返します(プログラミング言語でコンストラクタが値を返すことができる場合)
  3. 有効なオブジェクトを返しますが、そのパスが適切に設定されなかったことを示すフラグが付いています(ugh)
  4. 他の?

さまざまなプログラミング言語の「ベストプラクティス」がこれを異なる方法で実装すると思います。たとえば、ObjCは(2)を好むと思います。ただし、(2)は、コンストラクターが戻り型としてvoidを持たなければならないC ++で実装することは不可能です。その場合、(1)が使用されると考えます。

選択したプログラミング言語で、この問題の処理方法とその理由を説明できますか?


1
Javaの例外は、これを処理する1つの方法です。
マフムードホッサム

C ++コンストラクターは戻りませんvoid-オブジェクトを返します。
ギャブリン

6
@gablin:実際、それも厳密には真実ではありません。メモリを割り当てるためのnew呼び出しoperator new、そしてそれを埋めるためのコンストラクター。コンストラクターは何も返さず、newから取得したポインターを返しますoperator newvoidただし、「何も返さない」ということは、「返品する」ことを意味します。
ジョンパーディ

@Jon Purdy:うーん、理にかなっています。良い電話。
ギャブリン

回答:


8

コンストラクターに頼って汚い仕事をするのは決して良いことではありません。また、明示的なドキュメントがない限り(そしてクラスのユーザーがそれを読んだか、そう言われたのでない限り)、コンストラクターで作業が行われるかどうかは、他のプログラマーにとっても不明です。

例(C#の場合):

public Sprite
{
    public Sprite(string filename)
    {
    }
}

ユーザーがすぐにファイルをロードしたくない場合はどうなりますか?ファイルのオンデマンドキャッシュを実行する場合はどうなりますか?できません。bool loadFileコンストラクターに引数を入れることを考えるかもしれませんが、ファイルをロードするメソッド必要になるため、これは面倒Load()です。

現在のシナリオを考えると、これを行うにはクラスのユーザーにより柔軟で明確になるでしょう。

Sprite sprite = new Sprite();
sprite.Load("mario.png");

または、代わりに(リソースのようなものの場合):

Sprite sprite = new Sprite();
sprite.Source = "mario.png";
sprite.Cache();

// On demand caching of file contents.
public void Cache()
{
     if (image == null)
     {
         try
         {
             image = new Image.FromFile(Source);
         }
         catch(...)
         {
         }
     }
}

あなたの場合、load(..)が失敗した場合、まったく役に立たないオブジェクトがあります。私はスプライトなしでスプライトが欲しいとは思いません...しかし、質問は本当の質問よりも聖戦の主題のように見えます:)
avtomaton

7

Javaでは、例外を使用するか、ファクトリパターンを使用できます。これにより、nullを返すことができます。

Scalaでは、ファクトリメソッドからOption [Foo]を返すことができます。これはJavaでも機能しますが、もっと面倒です。


.NET言語でも例外またはファクトリを使用します。
ベスホワイトゼル

3

例外をスローします。

Nullを返すことができるかどうかをチェックする必要があります(チェックはされません)

これがチェック例外の対象です。あなたはそれが失敗する可能性があることを知っています。発信者はこれを処理する必要があります。


3

ではC ++、コンストラクタは、クラスの初期化/メンバーを作成するために使用されています。

この質問に対する正しい答えはありません。しかし、私がこれまで観察したことは、ほとんどの場合、このようなものをどのように処理するかを選択するクライアント(またはAPIを使用する人)であるということです。

時には、オブジェクトに必要な可能性のあるすべてのリソースをコンストラクターに割り当てて、何かが失敗した場合(オブジェクトの作成を中止)に例外をスローするか、コンストラクターでそれを行わずに作成が常に成功することを確認することがあります、これらのタスクをいくつかのメンバー関数に任せます。

動作を選択するのがあなた次第である場合、例外はエラーを処理するためのデフォルトのC ++の方法であり、可能な場合は使用する必要があります。


1

また、パラメーターなしでオブジェクトをインスタンス化することも、失敗しないことが確実なパラメーターのみでインスタンス化することもできます。その後、安全に例外をスローしたり、希望することを実行できる初期化関数またはメソッドを使用します。


6
さらに初期化を必要とする使用できないオブジェクトを返すことは、最悪の選択だと思います。これで、クラスを使用するたびにコピーするか、新しいメソッドに組み込む必要がある複数行のフラグメントができました。オブジェクトを構築できない場合は、コンストラクタにすべての作業を行わせ、例外をスローすることもできます。
ケビンクライン

2
@kevin:オブジェクトに依存します。失敗はおそらく外部リソースが原因であるため、エンティティは意図(ファイル名など)を登録する必要がありますが、完全に初期化するために繰り返し試行することができます。値オブジェクトの場合、私はおそらく基礎となるエラーをキャプチャできるエラーを報告することに傾いているので、おそらく(ラップ?)例外をスローします。
デイブ

-4

コンストラクターでクラスやリストを初期化しないことを好みます。必要なときにいつでもクラスまたはリストを初期化します。例えば。

public class Test
{
    List<Employee> test = null;
}

これをしないでください

public class Test
{
    List<Employee> test = null;

    public Test()
    {
        test = new List<Employee>();
    }
}

代わりに、必要なときに初期化してください。

public class Test
{
    List<Employee> emp = null;

    public Test()
    { 
    }

    public void SomeFunction()
    {
         emp = new List<Employee>();
    }
}

1
これは悪いアドバイスです。オブジェクトを無効な状態にしないでください。それがコンストラクタの目的です。複雑なロジックが必要な場合は、ファクトリパターンを使用します。
AlexFoxGill

1
クラスの不変条件を確立するためにコンストラクタがありますが、サンプルコードはそれを断言するのに役立ちません。
ニール
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.