web-dev-qa-db-ja.com

複数の無関係な値を返すクラス、これを達成するためのより良い方法はありますか?

「トラッカー」クラスがあります。このクラスは、指定された入力画像からオブジェクトを追跡します。しかし、これに加えて、画像の生成に使用される値に対応する別のパラメーターがあります。トラッカーは、次の反復でより良い結果を得るために、このパラメーターの新しい値を提案したい場合があります。これは、これまでにない新しい値生成画像を示唆しています。

私は現在これを実装しています:

_struct TrackResults{
    double new_scene_generation_coefficient;
    std::vector<Track> tracks;
}

class Tracker{
public:
    TrackResults update(const Image& image, double scene_generation_coefficient){
        // generate tracks and a new_scene_generation_coefficient 
        // ...
        return {new_scene_generation_coefficient, tracks}
    }
}
_

私の直感は、値を直接変更するのではなく、Trackerが1つの値のみを必要とし、変更される可能性があるクラスについて知ることに依存しないようにすることです。代わりにここで参照によって返された場合、トラッカーを使用するもののカスケード効果を他の方法では依存しないものに依存させたり、可変参照を危険に渡したりする可能性があると思います。

私はこれで保守性の点で問題を見つけませんが、いくつかの非常にわずかな人間工学的な問題があります。たとえば、トラックが必要な場合、最初にTrackResultオブジェクトを経由する必要があります。

ただし、これはコードの匂いのように見えるため、データクラスの値は互いに関係がなく、両方を組み合わせて使用​​するメソッドはありません。他の投稿では、クラスのメンバーで機能する関数をメソッドに移動できるだけなので、データクラスはないはずだと人々は主張しています。

私が言及すべきもう1つのことは、Tracksは内部的になんらかの方法で維持されているということです。トラックは値によって返され、外部のアクター(実際のシステムに存在するロガーなど)からのクラスの内部構造をいじることを回避します。これにより、updateの後にトラックを照会する必要があると思います。返されるのは_new_scene_generation_coefficient_だけです。ただし、潜在的に、最初にImageを生成したオブジェクトを編集するために使用する必要がある値が他にもありますが、少なくともこれらは関連しています。更新後の個別のトラッククエリの問題は、ユーザーが後で直接クエリしようとする保証はなく、実行したとしても、トラックリストの状態が次のように取得されるという保証がないためです。 update()が呼び出されたときです。これは、この方法で別のメソッドを使用するために、ユーザーが最初に別のメソッドを呼び出すことを要求するコードのにおいのようです。

7
whn

簡単に掃除できるようです。繰り返し学習する手順があります。呼び出されるたびに、パラメーターの品質が少し向上します。その手順が初めて呼び出されたときに、最初にいくつかの球場図が必要です。

したがって、initial_scene_generation_coefficientをTrackerのコンストラクターに渡し、それをメンバー値scene_generation_coefficientとして保存できます。 Updateの引数リストから引数を削除し、代わりにメンバーを使用します。メンバーは呼び出しごとに更新されます。

生成係数はTracker専用です。好奇心を満たす(デバッグ)以外に、Trackerの外部で使用できるようにする理由はありません。

スレッドセーフにする場合は、Updateのコードをロックする必要があります。

[編集]

わかりました、あなたのコメントの後で今それを得ます。私が見た間違いは、トラッカーが画像生成について理解していることになっているということです。それは意味がなく、SRPに違反しています。トラッカーは画像内のオブジェクトを認識するだけです。画像の品質を評価する方法はありません。人間の目、または認識されたオブジェクトの数が異なる可能性のある多くの反復が必要です。誤検知を除外すると、認識されたオブジェクトが多くなるほど、画質は良くなったと言えます。さまざまな照明条件で生成された一連の画像をトラッカーに送り、追跡結果を結合したいようです。一部の画像では常に結果が得られないため、それらを生成する手間を省くことができます(やりがいのない生成係数を使用するため)。ただし、係数はトラッカーのコンテキストでは意味がないため、渡したり戻したりすることはありません。

認識されたオブジェクトを取得し、新しく生成された画像を試し、トラッカーからさらにオブジェクトが返されるかどうかを確認します。暗い画像と明るい画像の両方をフィードして、どちらかがより多くのオブジェクトを生成するかどうかを確認できます。これは、オブジェクトが再び減るまで、係数を使用して進む方向です。その後、あなたはあなたの最適に合格します。

3
Martin Maat

最初に、最新の結果を得るために常にそのパラメーターを取得して渡す必要がある場合、ここでの「無関係」の考え方は少しあいまいだと思います。正確にはわかりませんscene_generation_coefficientは、クライアントが基本的にその状態を追跡し、常にオブジェクトに渡し、結果を取得し、結果を取得しなければならないことを除いて、実行することになっています。

これは基本的に、クライアント(Trackerを使用する人)に対して状態管理の責任を外部化することです。そして、場合によってはそうする正当な理由がありますが、正当な理由のほとんどは、より手続き的または機能的なコンテキストにある傾向があると思います。

OOPを使用している場合、オブジェクトが実用的なニーズに対応する際の重要なポイントの1つは、このようなことを回避することです。したがって、代わりの設計の1つは、Trackerに係数を直接保存および更新し、これらのオブジェクトを異なるコンテキストで使用する必要がある場合は、複数のトラッカーをインスタンス化することです。完全に異なる係数が同時に異なる呼び出し関数またはスレッドで同時に使用されているときに同じトラッカーを再利用したい場合は、おそらくTrackerResultsに責任を持たせることができます。

class TrackerResults
{
public:
    TrackerResults(...) {}

    // Even the image parameter might be a member passed through ctor
    // depending on design requirements.
    void update(const Image& image)
    {
        *this = tracker->update(image, scene_generation_coefficient);
    }

    const Track& operator[](size_t n) const 
    {
         return tracks[n];
    }

private:
    // Can use something like shared_ptr for robustness alternatively.
    Tracker* tracker;

    // Manage this internally in the object itself so the client doesn't
    // have to constantly retrieve and pass it back in.
    double scene_generation_coefficient;

    std::vector<Track> tracks;
};

...大雑把なイラストとしてはこのようなもの。次に、ユーザーはこれらのTrackerResultsオブジェクトを1つ以上作成し、updateを繰り返し呼び出して、この係数の状態を手動で管理する必要なく、改善された結果を取得し続けます。このようなもの。クライアントが新しい係数の「提案」を自由に無視できる場合、クライアントは係数を外部で追跡する可能性があり、オーバーロードを使用してその機能をTrackResultsにプログラムできますが、ほとんどの場合、クライアントがその状態を管理する必要がないように、パラメーターを介して係数を常に渡して結果の係数を取得する必要がないようにします。

ここではバランスをとる行為があり、それが常に単純であるとは限りませんが、違反する前に開始する実際的なルールは、クライアントにOOPに関して必要以上の状態を管理させないことです。クライアントがクラス自体の外部でコンテナーのサイズを維持する必要があるコンテナークラスを設計したくない場合は、常に渡して、コンテナーのサイズを変更できる関数で新しいサイズを取得する必要があります。非常に露骨で極端な例として。

ここで副作用を回避することの懸念を理解できますが、オブジェクトを使用すると、副作用が避けられないような種類の状態をカプセル化することで、多くの便利さが得られます。オーバーラップ/上書きおよび競合状態を回避するには、1つの整数変数を使用してすべての整数出力をキャプチャしないのと同じ方法で、そのタイプの複数のオブジェクトを使用することになります。

更新後の個別のトラッククエリの問題は、ユーザーが後で直接クエリしようとする保証はなく、実行したとしても、トラックリストの状態が次のように取得されるという保証がないためです。 update()が呼び出されたときです。

先に進んで、同じ考えの別の懸念を挙げます。これは、係数の共有状態がトラッカーをロックしないとスレッドセーフでなくなるということです。ただし、これは、各インスタンスが独自の係数を維持する複数のオブジェクトインスタンスを使用することで簡単に対処できる問題です。これはOOPの方法であり、これらのタイプの関数は単一の副作用を引き起こすことが多い方法ですが、クラスにより不変条件などを簡単に維持できるようになります。クライアントを変更せずにこれをロックすることもできます。何らかの理由で共有オブジェクトと同じオブジェクトを使用する必要がある場合は、コードを記述します。

これで保守性の点で問題を見つけることはできませんが、非常にわずかな人間工学的な問題があります。たとえば、トラックが必要な場合は、最初にTrackResultオブジェクトを経由する必要があります。

人間工学はC++のような言語でのろわれます。ボイラープレートを最小限に抑えるなどのことをしようとすると、一般に、誤解を招く可能性が低い、より簡単なコード(代替の場合)を優先することに対して逆効果になります。それでも提案された回答を使用すると、より人間工学的な解決策と、シーン係数が各update呼び出し。

0
Dragon Energy