简体   繁体   中英

Which design patterns to use to implement this business logic?

I am trying to determine the best design pattern to use for a business key validation web service. The basic logic flow is coded below. The program will take a parameter and use a field to help determine the path to take in searching multiple systems where this business key can be found. System1 is searched first, if not found, search System2 and System3. The System1 search logic depends on a field in the parameter passed into the original validation method.

I am not exactly sure which design pattern to use. It looks like Command, Chain of Responsibility, Template Method all could be used here.

With my implementations below, I see the following problems:

  1. Each SearchSystemX method needs to know to return null if the business key is not found so that the "control" method will continue to search other systems.

  2. Each SearchSystemX must know how to populate the business object, currently just implemented by a simple primitive string, but that is for example only.

Please let me know your thoughts.

public string Validate (string parms) {
 string returnValue = null;

 returnValue = SearchSystem1(parms);

 if (returnValue == null) {
  returnValue = SearchSystem2(parms);

  if (returnValue != null) {
   returnValue = SearchSystem3(parms);
  }
  else if (returnValue == null) {
   if (parms == "Criteria1") {
    returnValue = SearchSystem4(parms);

    if (returnValue == null) {
     throw new ApplicationException("ID Invalid");
    }
   }
   else if (parms == "Criteria2") {
    throw new ApplicationException("ID Invalid");
   }
  }
 }
 return returnValue;

private string SearchSystem1 (string parms) {
 string returnValue = null;

 if (parms == "Criteria1") {
  returnValue = SearchSystem1UsingColumn1(parms);
 }
 else if (parms == "Criteria2") {
  returnValue = SearchSystem1UsingColumn2(parms);

  if (returnValue == null) {
   returnValue = SearchSystem1UsingColumn4(parms);
  }
 }

 if (returnValue != null) {
  returnValue = FetchXYCoordinate(parms);
 }

 return returnValue;
}

Thanks!

Chain of responsability

Each processing object contains a set of logic that describes the types of command objects that it can handle, and how to pass off those that it cannot to the next processing object in the chain

So you define and abstract SearchSystem ( or SystemSearch ) and add subclasses like this:

class SearchSystem
{
    //link to the next in the chain 
    SearchSystem next;

    // Basic search, If cannot handle forward to the next.
    public Result search( Criteria c ) 
    {
        Result r = doSearch( c );

        if( r != null ) 
        {
            return r;
        }

        return next.search( c );
    }
    // subclass specific system search
    abstract Result doSearch( Criteria c );
}

class SearchSystemOne: SearchSystem 
{
    Result doSearch( Criteria c )
    {
        // do some system 1 speficif stuff 
        // and return either and instance or null
    }
}
class SearchSystemTwo: SearchSystem 
{
    Result doSearch( Criteria c )
    {
        // do some system 2 speficif stuff 
        // and return either and instance or null
    }
}
.... etc etc. 
// End of the chain
class SearchSystemOver: SearchSystem 
{
    public Result search( Criteria c ) 
    {
        throw new ApplicationException("Criteria not foud", c );
    }
    Result doSearch( Criteria c )
    {
       // didn't we throw exception already?
    }
}

Instantiate

SearchSystem one = new SearchSystemOne();
SearchSystem two = new SearchSystemTwo();
SearchSystem three = new SearchSystemThree();
SearchSystem four = new SearchSystemFour();
SearchSystem over = new SearchSystemOver();

And build the chain

one.next = two;
two.next = three;
three.next = four;
four.next = over;

And finally search it.

SearchSystem searcher = one;

searcher.search( Criteria.addName("Oscar").addLastName("Reyes"));

Possibly the Strategy Pattern .

It allows you to abstract away the algorithms you're using to perform the logic, encapsulate them in their own objects, and then use them interchangeably throughout your application (allow the caller to define which algorithm to use).

I'd probably use the strategy pattern here by defining an interface for the search system(s):

public interface ISearchStrategy
{
    string Search(string criteria);
}

and then passing them to the validate method (although the validation class could get them from somewhere else).

public string Validate(string parms, IEnumerable<ISearchStrategy> searchStrategies)
{
    string returnValue = null;

    foreach(var strategy in searchStrategies)
    {
        if(returnValue == null)
        {
            returnValue = strategy.Search(parms);
        }
    }

    if(returnValue == null) throw new ApplicationException("ID invalid");
    return returnValue;
}

Without knowing more about your domain, I can only suggest minor tweaks. First would be to use guard clauses .

Also you mention how the SearchSystems need to know how to build a business object. I would make sure the SearchSystems can be treated polymorphically (that way you have a path to refactoring out a strategy or some kind of dependency injection). Also I would favor a SystemSearcher over a family of SearchSystem methods. Then I would inject into the SearchSystems a business object factory. I would also design the behavior with TryXXX semantics.

public interface ISearchSystem
{
  bool TryGet(string parms, out string businessObject);
}

public class System1Searcher : ISearchSystem
{
  public System1Searcher(BusinessObjectFactory factory) { _factory = factory; }
  private field BusinessObjectFactory _factory;
  public bool TryGet(string parms, out string businessObject)
  {
    // do internal logic
    return _factory.Create(string stuff);
  }
}

public class Service
{
  // is "Validate" the correct metaphor for this logic?
  public string Validate(string parms) 
  {
    string returnValue = null;
    if (new System1Searcher().TryGet(parms, out returnValue)
      return returnValue;

    if (new System2Searcher().TryGet(parms, out returnValue)
      return returnValue;

    if (new System3Searcher().TryGet(parms, out returnValue)
      return returnValue;

    if (new System4Searcher().TryGet(parms, out returnValue)
      return returnValue;

    // this message should be written in terms of the logic of this method
    // such as "Parameters invalid"
    throw new ApplicationException("ID Invalid"); 
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM