2016-12-22 13 views
2

私はisValidメソッドが呼び出されている下のクラスを持っています。単一の責任を使用してメソッドを書く方法は?

  • 私はisValid方法でRecordオブジェクトからいくつかを抽出しようとしています。そして、私はそれらのフィールドのいくつかを検証しています。有効な場合は、holderマップにいくつかのフィールドを追加してから、DataHolderビルダークラスにデータを入力し、最後にDataHolderクラスを返します。
  • 有効でない場合はnullを返します。以下は

私のクラスである:

public class ProcessValidate extends Validate { 
    private static final Logger logger = Logger.getInstance(ProcessValidate.class); 

    @Override 
    public DataHolder isValid(String processName, Record record) { 
    Map<String, String> holder = (Map<String, String>) DataUtils.extract(record, "holder"); 
    String deviceId = (String) DataUtils.extract(record, "deviceId"); 
    Integer payId = (Integer) DataUtils.extract(record, "payId"); 
    Long oldTimestamp = (Long) DataUtils.extract(record, "oldTimestamp"); 
    Long newTimestamp = (Long) DataUtils.extract(record, "newTimestamp"); 
    String clientId = (String) DataUtils.extract(record, "clientId"); 

    if (isValidClientIdDeviceId(processName, deviceId, clientId) && isValidPayId(processName, payId) 
     && isValidHolder(processName, holder)) { 
     holder.put("isClientId", (clientId == null) ? "false" : "true"); 
     holder.put("isDeviceId", (clientId == null) ? "true" : "false"); 
     holder.put("abc", (clientId == null) ? deviceId : clientId); 
     holder.put("timestamp", String.valueOf(oldTimestamp)); 

     DataHolder dataHolder = 
      new DataHolder.Builder(record).setClientId(clientId).setDeviceId(deviceId) 
       .setPayId(String.valueOf(payId)).setHolder(holder).setOldTimestamp(oldTimestamp) 
       .setNewTimestamp(newTimestamp).build(); 
     return dataHolder; 
    } else { 
     return null; 
    } 
    } 

    private boolean isValidHolder(String processName, Map<String, String> holder) { 
    if (MapUtils.isEmpty(holder)) { 
     // send metrics using processName 
     logger.logError("invalid holder is coming."); 
     return false; 
    } 
    return true; 
    } 

    private boolean isValidpayId(String processName, Integer payId) { 
    if (payId == null) { 
     // send metrics using processName  
     logger.logError("invalid payId is coming."); 
     return false; 
    } 
    return true; 
    } 

    private boolean isValidClientIdDeviceId(String processName, String deviceId, String clientId) { 
    if (Strings.isNullOrEmpty(clientId) && Strings.isNullOrEmpty(deviceId)) { 
     // send metrics using processName  
     logger.logError("invalid clientId and deviceId is coming."); 
     return false; 
    } 
    return true; 
    } 
} 

は多くのことをやって、私のisValid方法ですか?それは複数の部分で分解できますか?あるいは、そのコードを書く良い方法はありますか?

また、レコードが有効でない場合はnullを返すelseブロックにあるコードでは気にしません。私はそれがはるかに良い方法で書くことができると確信しています。

アップデート:私の場合は

が、これは私がやっていたものです。私はこのようにそれを呼び出しています:

Optional<DataHolder> validatedDataHolder = processValidate.isValid(processName, record); 
if (!validatedDataHolder.isPresent()) { 
    // log error message 
} 
// otherwise use DataHolder here 

だから今、それは私がこのようにしなければならない意味します

boolean validatedDataHolder = processValidate.isValid(processName, record); 
if (!validatedDataHolder) { 
    // log error message 
} 
// now get DataHolder like this?  
Optional<DataHolder> validatedDataHolder = processValidate.getDataHolder(processName, record); 
+1

"有効でない場合は、nullを返します。" - 読んでください:http://stackoverflow.com/questions/1274792/is-returning-null-bad-design – alfasin

+3

* working *コードについては、codereview.stackexchange.comが良い場所かもしれません。 – GhostCat

+0

@GhostCat彼はコードレビューを要求しなかったが、彼はこの実装に関する特定の問題を指摘し、解決方法を尋ねた。コードレビューで投稿しても問題ありません。特定のプログラミング上の問題を扱っているので、ここに投稿することもできます。 – alfasin

答えて

2

あなたはisValid()があまりにも多くのことをやっている正しいです。しかし、それだけでなく、ほとんどの人がisValid()というメソッドを見ると、ブール値が返されると予想されます。この場合、私たちは戻ってきて、反直観的なDataHolderのインスタンスです。

は、たとえば、あなたがこの方法で物事を分割するようにしてください:

public static boolean isValid(String processName, Record record) { 
    return isValidClientIdDeviceId(processName, record) && 
      isValidPayId(processName, record) && 
      isValidHolder(processName, record); 
} 

し、別の方法でDataHolderを構築し、言う:

public static Optional<DataHolder> getDataHolder(String processName, Record record) { 
    Optional<DataHolder> dataHolder = Optional.empty(); 
    if (isValid(processName, record)) { 
     dataHolder = Optional.of(buildDataHolder(processName, record)); 
     // ... 
    } 
    return dataHolder; 
} 

それはあなたのプログラムが容易になります読んで維持するために!

+0

これはかなりいいようですが、ちょっと混乱しています。私の場合、私はレコードを検証し、 'DataHolder'も送り返しています。レコードが有効であれば、DataHolder Builderを送信します。それ以外の場合はnullを返します。あなたの例では、DataHolderを分割すると、DataHolderはどのように返されますか? – john

+0

@david私はこの質問に答えることができない要件に精通していないので、 'null'を返すのは悪い習慣です。戻り値の型として' Optional < DataHolder> 'を使うことをお勧めします。私はそれを答えに加えます。 Optionalを使うとより安全です。結果が得られたら、 'dataHolder.isPresent()'が安全かどうかを確認し、それに応じて行動してください。 – alfasin

+0

私の場合、要件は、レコードオブジェクトが与えられた場合、それらを抽出することによって、いくつかのフィールドを検証します。有効であればDataHolderオブジェクトに返すようにし、何らかの理由で有効でない場合は 'Optional.absent();'を返します。これは私が主にやりたいことです。そして、それは私がやっていることですが、複雑なやり方です。 – john

2

の名前はとなります。

alfasinが正しく指摘しているとおり、非公式大会ははisValid(命名法)ブール値を返さなければならないことです。本当にDataHolderを返すことを検討している場合。

DataHolder fetchHolderWithChecks(String processName, Record ...) 

そして、私がnullを返さないだろう - のいずれかのオプション;:私の提案は、このように、(ビットと意味)の名前を変更することであろう単に例外をスローします。あなたは、発生したエラーについてあなたのユーザーに伝えたくありませんか?したがって、例外を投げるときには、エラーメッセージをより高いレベルに提供することになります。検証自体に

interface OneAspectValidator { 
    void check(... // if you want to throw an exception 
    boolean isValid(... // if you want valid/invalid response 

そしてそのインターフェイスの後、様々な実装:私は、多くの場合、このようなものを使用。

そして、「検証のエントリポイントは」何とかこれらの側面を一つずつ検証するためにリストし、最終的反復

private final static List<OneAspectValidator> validators = ... 

のように、リストを作成します。

このアプローチの素晴らしい点は、1つの専用クラス内で1種類の検証用のコードがあることです。検証を簡単に強化することができます。新しいimplクラスを作成するだけです。その既存のリストに対応するオブジェクトを追加します。

+0

それは命名についての良い点です。この場合にも、私の 'fetchHolderWithChecks'メソッドは、まだそのコードのすべてに悩まされていますか?私がそのメソッドからDataHolderを返すだけなら、それを改善できると思いますか? – john

+0

これは私の2番目の部分についてです:そのメソッドは、バリデータのリストを反復し、それらを呼び出すとき...コードははるかに簡単になります。あなたは**間違いなく一つの方法ですべてのこれらの小切手を持つことを望んでいません。そして、オブジェクトではなくメソッド内でこれらのチェックがある限り、他のすべてのチェックメソッドを1つずつ呼び出さない方法があります。 – GhostCat

+0

私はこのパターンを以前に使っていません。これを使用する方法の例を示すことができれば、私のコード内でこれを使う方法をちょっと混乱させているので、理解を深めるのに役立ちます。 – john

1

これは直接実行可能ではないかもしれませんが、このコードをクリーンアップする場合はまずオブジェクト指向(オブジェクト指向)を使用することをお勧めします。 OOを適切に使用していない場合は、SRPのように、OOの詳細を議論する必要はありません。

私が言っていることは、コードがであることを伝えることができませんでした。あなたのクラス名は "ProcessValidate"(それは事でさえ?)、 "Record"、 "DataHolder"です。それはかなり疑わしいところです。

文字列リテラルは、あなたの識別子よりもドメイン( "payId"、 "deviceId"、 "clientId")の詳細を明らかにしますが、それは良い兆候ではありません。

あなたのコードは、物事を行うように依頼するのではなく、他の "オブジェクト"からデータを取り出すことに関するものです(オブジェクト指向の特徴)。

概要:ドメインを反映するオブジェクトにコードをリファクタリングしてみてください。これらのオブジェクトが責任に固有のタスクを実行するようにします。オブジェクトから情報を取得しないようにしてください。情報をオブジェクトに設定しないようにしてください。それが完了すると、SRPがどんなものであるかがはっきりします。

関連する問題