Extend (not change) search class for new behaviour

2019-06-02 14:38发布

问题:

I'm making a change to an API that serves data (this is an update to my original question). Some of the searches require data about an author and take a IAuthor object. The API has an IAuthor interface and a single concrete class that implements IAuthor called Author.

I need to change the behaviour of the Search.GetBooksByAuthor method to give different semantic when the author is flagged as a novelist. I've heard about the open/closed principle and it would seem that changing the IAuthor and/or Author and/or Search classes would violate this (the Book class is definitely remaining unchanged, though). How then to make this simple change?

For example, I was originally thinking something like this but my thinking is probably wonky because it involves changing the Search class:

//Before
class Search 
{
   public Books[] GetBooks(IAuthor author){
       // Call data access GetBooks...
    }
}


//After
class Search
{
   public Books[] GetBooks(IAuthor author){
       // To maintain pre-existing behaviour
       // call data access GetBooks method with SQL param @AsNovelist = false...
       // (or don't pass anything because the SQL param defaults to false)
    }

   public Books[] GetBooksAsNovelist(IAuthor author){
       // To get new behaviour
       // call data access GetBooks method with SQL param @AsNovelist = true
       // so that their non-fiction books are omitted from results
    }
}

回答1:

It may seem obvious that something has to change to cater for knowing whether or not your author is a Novelist, you could do this one of two ways. You don't have to change anything in theory, you do however need a new class.

public class Novelist : Author, IAuthor { }

Then you can pass a novelist into your method and then deterimne your type of author.

class Search 
{
   public Books[] GetBooks(IAuthor author){
       if(author is Novelist)
         //Do some stuff or set a flag/bool value
    }
}

OR as previously mentioned, implement a boolean member to your Author interface and check that. The above would prevent you changing your class structures however.

This means that your novelist is in fact still an author, it just has it's own type. Your method signatures remain the same, your class structures remain the same you just have a type for a "different type of author", which should in theory be fine. Call as below to test.

GetBooks(new Novelist());


回答2:

How about using a predicate for filtering?

class Search
{
   public Books[] GetBooks(IAuthor author, Func<IAuthor, bool> filter){
       // ...
    }
}

search.GetBooks(author, a => a.IsNovelist)


回答3:

In order to extend classes C# .NET introduced extension methods in .NET 3.5 whose main purpose is precisely to extend a class without modifying the existing code:

public static class SearchExtensions
{
  public static Books[] GetBooksAsNovelist(this Search search, IAuthor author)
  {
     // Perform novelist search
  }
}

Then you can invoke your Search class normally with:

Search.GetBooksAsNovelist(author);


回答4:

You can use the Extension feature of C# language.

Please see http://msdn.microsoft.com/en-us/library/vstudio/bb383977.aspx

Extensions enable to add functionality to class by keeping the class intact.

In your case you can write as:

public static class SearchExtensions
    {
        public static Books[] GetBooks(this Search search, IAuthor author)
        {
            //new logic
        }
    }   

You can access this new method by Search object and Search class also remains intact.

Please let me know if you find this helpful.



回答5:

You could make your class partial to be able to add functionalyti without extensions, or inheritance, or inversion of control:

// file: Search.cs

partial class Search
{
    public Books[] GetBooks(IAuthor author) { ... }
}

// file: Search.Advanced.cs

partial class Search
{
    public Books[] GetBooksAsNovelist(IAuthor author) { ... }
}

Results:

http://i.snag.gy/VowNv.jpg



回答6:

Keep the search class methods as virtual thus anyone can override them creating a new behavior?



标签: c# oop interface