値チェッカー関数はブール値とメッセージの両方を返す必要があります


14

私は、文字列で渡される値確認機能を持っています。これは、クレジットカード番号確認機能によく似ており、値が正しい形式であることを確認する必要があります。

適切な形式の場合は、trueを返す必要があります。

正しい形式でない場合は、falseを返す必要があり、値の何が問題なのかも教えてください。

問題は、これを達成する最も良い方法は何ですか?

以下にいくつかの解決策を示します。

1.整数/列挙戻りコードを使用して、意味を示します。

String[] returnCodeLookup = 
[
"Value contains wrong number of characters, should contain 10 characters",
"Value should end with 1", 
"Value should be a multiple of 3"
]

private int valueChecker(String value)
{
    /*check value*/
    return returnCode;
}

rc = checkValue(valueToBeChecked);
if rc == 0
{
    /*continue as normal*/
}
else
{
    print("Invalid value format: ") + returnCodeLookup[rc];
}

このソリューションには、呼び出し側での実装が必要なので、このソリューションは好きではありません。

2. returnCodeクラスを作成します

Class ReturnCode()
{
    private boolean success;
    private String message;

    public boolean getSuccess()
    {
        return this.success;
    }

    public String getMessage()
    {
        return this.message; 
    }
}

private ReturnCode valueChecker(String value)
{
    /*check value*/
    return returnCode;
}

rc = checkValue(valueToBeChecked);
if rc.getSuccess()
{
    /*continue as normal*/
}
else
{
    print("Invalid value format: ") + rc.getMessage();
}

この解決策はきちんとしていますが、やり過ぎ/車輪の再発明のようです。

3.例外を使用します。

private boolean valueChecker(String value)
{
    if int(value)%3 != 0 throw InvalidFormatException("Value should be a multiple of 3";
    /*etc*/
    return True;
}

try {
rc = checkValue(valueToBeChecked);
}

catch (InvalidFormatException e)
{
     print e.toString();
}

このソリューションを使用したいのですが、ビジネスロジックに例外を使用しないでくださいと言われました。


'[..]値が正しい形式であることを確認してください。名前はFormatCheckerではないでしょうか?
アンディ

真/偽の結果は冗長なようです。成功を示すために空またはnullの文字列を単に返すことができますか?それは約50年間UNIXで機能していました。:-)
user949300

回答:


14

両方の懸念をカプセル化する、より複雑な戻りオブジェクトを使用します。例:

public interface IValidationResult {
  boolean isSuccess();
  String getMessage();
}

これにはいくつかの利点があります。

  1. 1つのオブジェクトに関連する複数のデータを返します。
  2. 将来さらにデータを追加する必要がある場合は、拡張の余地があります。
  3. 時間的結合に依存しない:複数の入力を検証でき、他の回答のようにメッセージを破壊しません。スレッド間でも、任意の順序でメッセージを確認できます。

検証が単なるtrueまたはfalse以上のものである可能性があるアプリケーションで、私は以前にこの特定の設計を実際に使用しました。おそらく、詳細なメッセージが必要であるか、入力の一部のみが無効です(たとえば、10個の要素を持つフォームには1つまたは2つの無効なフィールドしかない場合があります)。この設計を使用すると、これらの要件に簡単に対応できます。


私はこのソリューションが私のソリューションよりも優れていることを認めなければなりません。私はスレッドセーフではありません。
Tulainsコルドバ

@ user61852これは結果オブジェクトのインターフェースの高レベルの概要ですが、ここでの目標は、検証コードが状態を含まない独自のオブジェクトになることだと思います。これにより不変になり、このサイトで何度も説明する多くの利点があります。

なぜインターフェイスが必要なのですか?
dwjohnston

1
@dwjohnstonインターフェイスは必要ありませんが、良いアイデアです。継承は、必要な場合にのみ使用すべき非常に強力なタイプの結合です。

または、さらに簡略化できます。成功は面白くないのでIValidationResult.SUCCESS、空のエラーメッセージを返す定数を宣言します。次に、ロジックは次のようになりますif (result != SUCCESS) { doStuff(result.getMessage()); }
モルゲン

2

上記のいずれでもない、ValueCheckerクラスを使用する

最初に柔軟性を提供するインターフェイス:

public interface IValueChecker {
    public boolean checkValue(String value);
    public String getLastMessage();
}

次に、必要な数の値チェッカーを実装します。

public class MyVeryEspecificValueChecker implements IValueChecker {
    private String lastMessage="";
    @Override
    public boolean checkValue(String value) {
        boolean valid=false;
        // perform check, updates "valid" and "lastMessage"
        return valid;
    }
    @Override
    public String getLastMessage() {
        return lastMessage;
    }
}

サンプルクライアントコード:

public class TestValueChecker {
    public static void main(String[] args) {
        String valueToCheck="213123-YUYAS-27163-10";
        IValueChecker vc = new MyVeryEspecificValueChecker();
        vc.checkValue(valueToCheck);
        System.out.println(vc.getLastMessage());
    }
}

多くの異なる値チェッカーを使用できるという利点があります。


1
最後にチェックされた値を確認する方法がないまま、値チェッカーが状態を保持するのが好きかどうかはわかりません。
ピーターK.

1

私の答えは、@ Snowmanのアプローチを拡張します。基本的に、すべての検証、すべてのビジネスルール、およびすべてのビジネスロジックは、少なくともWebアプリケーションでは何らかの応答をもたらすことができるはずです。次に、この応答が発信者に表示されます。これにより、次のインターフェイスに移動しました(phpですが、質問は本質的に言語に依存しません)。

interface Action
{
    /**
     * @param Request $request
     * @throws RuntimeException
     * @return Response
     */
    public function act(Request $request);
}

ステートメントではなく式のように動作するスイッチ演算子を作成すると、アプリケーションサービスは次のようになります。

class MyApplicationService implements Action
{
    private $dataStorage;

    public function __construct(UserDataStorage $dataStorage)
    {
        $this->dataStorage = $dataStorage;
    }

    public function act(Request $request)
    {
        return
            (new _SwitchTrue(
                new _Case(
                    new EmailIsInvalid(),
                    new EmailIsInvalidResponse()
                ),
                new _Case(
                    new PasswordIsInvalid(),
                    new PasswordIsInvalidResponse()
                ),
                new _Case(
                    new EmailAlreadyRegistered($this->dataStorage),
                    new EmailAlreadyRegisteredResponse()
                ),
                new _Default(
                    new class implements Action
                    {
                        public function act(Request $request)
                        {
                            // business logic goes here

                            return new UserRegisteredResponse();
                        }
                    }
                )
            ))
                ->act($request)
            ;
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.