web-dev-qa-db-ja.com

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

文字列で渡される、クレジットカード番号チェック関数によく似た値チェック関数があり、値が正しい形式であることをチェックする必要があります。

それが正しいフォーマットであれば、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();
}

この解決策はきちんと整えられていますが、それはやりすぎ/再発明のようです。

。例外を使用します。

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();
}

私はこのソリューションを使いたくなりますが、ビジネスロジックに例外を使用するべきではないと言われています。

15
dwjohnston

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

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

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

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

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

15
user22815

上記のいずれでもない、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());
    }
}

これには、さまざまな値チェッカーを使用できるという利点があります。

2

私の答えは、@スノーマンのアプローチを拡張したものです。基本的に、すべての検証、すべてのビジネスルール、およびすべてのビジネスロジックは、少なくとも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)
            ;
    }
}
1
Zapadlo