Why do most system architects insist on first programming to an interface?

后端 未结 16 2270
离开以前
离开以前 2020-12-05 00:32

Almost every Java book I read talks about using the interface as a way to share state and behaviour between objects that when first \"constructed\" did not seem to share a r

相关标签:
16条回答
  • 2020-12-05 00:59

    I think one of the reasons abstract classes have largely been abandoned by developers might be a misunderstanding.

    When the Gang of Four wrote:

    Program to an interface not an implementation.

    there was no such thing as a java or C# interface. They were talking about the object-oriented interface concept, that every class has. Erich Gamma mentions it in this interview.

    I think following all the rules and principles mechanically without thinking leads to a difficult to read, navigate, understand and maintain code-base. Remember: The simplest thing that could possibly work.

    0 讨论(0)
  • 2020-12-05 01:00

    One reason is that interfaces allow for growth and extensibility. Say, for example, that you have a method that takes an object as a parameter,

    public void drink(coffee someDrink) {

    }

    Now let's say you want to use the exact same method, but pass a hotTea object. Well, you can't. You just hard-coded that above method to only use coffee objects. Maybe that's good, maybe that's bad. The downside of the above is that it strictly locks you in with one type of object when you'd like to pass all sorts of related objects.

    By using an interface, say IHotDrink,

    interface IHotDrink { }

    and rewrting your above method to use the interface instead of the object,

    public void drink(IHotDrink someDrink) {

    }

    Now you can pass all objects that implement the IHotDrink interface. Sure, you can write the exact same method that does the exact same thing with a different object parameter, but why? You're suddenly maintaining bloated code.

    0 讨论(0)
  • 2020-12-05 01:06

    There are some excellent answers here, but if you're looking for a concrete reason, look no further than Unit Testing.

    Consider that you want to test a method in the business logic that retrieves the current tax rate for the region where a transaction occurrs. To do this, the business logic class has to talk to the database via a Repository:

    interface IRepository<T> { T Get(string key); }
    
    class TaxRateRepository : IRepository<TaxRate> {
        protected internal TaxRateRepository() {}
        public TaxRate Get(string key) {
        // retrieve an TaxRate (obj) from database
        return obj; }
    }
    

    Throughout the code, use the type IRepository instead of TaxRateRepository.

    The repository has a non-public constructor to encourage users (developers) to use the factory to instantiate the repository:

    public static class RepositoryFactory {
    
        public RepositoryFactory() {
            TaxRateRepository = new TaxRateRepository(); }
    
        public static IRepository TaxRateRepository { get; protected set; }
        public static void SetTaxRateRepository(IRepository rep) {
            TaxRateRepository = rep; }
    }
    

    The factory is the only place where the TaxRateRepository class is referenced directly.

    So you need some supporting classes for this example:

    class TaxRate {
        public string Region { get; protected set; }
        decimal Rate { get; protected set; }
    }
    
    static class Business {
        static decimal GetRate(string region) { 
            var taxRate = RepositoryFactory.TaxRateRepository.Get(region);
            return taxRate.Rate; }
    }
    

    And there is also another other implementation of IRepository - the mock up:

    class MockTaxRateRepository : IRepository<TaxRate> {
        public TaxRate ReturnValue { get; set; }
        public bool GetWasCalled { get; protected set; }
        public string KeyParamValue { get; protected set; }
        public TaxRate Get(string key) {
            GetWasCalled = true;
            KeyParamValue = key;
            return ReturnValue; }
    }
    

    Because the live code (Business Class) uses a Factory to get the Repository, in the unit test you plug in the MockRepository for the TaxRateRepository. Once the substitution is made, you can hard code the return value and make the database unneccessary.

    class MyUnitTestFixture { 
        var rep = new MockTaxRateRepository();
    
        [FixtureSetup]
        void ConfigureFixture() {
            RepositoryFactory.SetTaxRateRepository(rep); }
    
        [Test]
        void Test() {
            var region = "NY.NY.Manhattan";
            var rate = 8.5m;
            rep.ReturnValue = new TaxRate { Rate = rate };
    
            var r = Business.GetRate(region);
            Assert.IsNotNull(r);
            Assert.IsTrue(rep.GetWasCalled);
            Assert.AreEqual(region, rep.KeyParamValue);
            Assert.AreEqual(r.Rate, rate); }
    }
    

    Remember, you want to test the business logic method only, not the repository, database, connection string, etc... There are different tests for each of those. By doing it this way, you can completely isolate the code that you are testing.

    A side benefit is that you can also run the unit test without a database connection, which makes it faster, more portable (think multi-developer team in remote locations).

    Another side benefit is that you can use the Test-Driven Development (TDD) process for the implementation phase of development. I don't strictly use TDD but a mix of TDD and old-school coding.

    0 讨论(0)
  • 2020-12-05 01:06

    In one sense, I think your question boils down to simply, "why use interfaces and not abstract classes?" Technically, you can achieve loose coupling with both -- the underlying implementation is still not exposed to the calling code, and you can use Abstract Factory pattern to return an underlying implementation (interface implementation vs. abstract class extension) to increase the flexibility of your design. In fact, you could argue that abstract classes give you slightly more, since they allow you to both require implementations to satisfy your code ("you MUST implement start()") and provide default implementations ("I have a standard paint() you can override if you want to") -- with interfaces, implementations must be provided, which over time can lead to brittle inheritance problems through interface changes.

    Fundamentally, though, I use interfaces mainly due to Java's single inheritance restriction. If my implementation MUST inherit from an abstract class to be used by calling code, that means I lose the flexibility to inherit from something else even though that may make more sense (e.g. for code reuse or object hierarchy).

    0 讨论(0)
  • 2020-12-05 01:08

    Programming to interfaces provides several benefits:

    1. Required for GoF type patterns, such as the visitor pattern

    2. Allows for alternate implementations. For example, multiple data access object implementations may exist for a single interface that abstracts the database engine in use (AccountDaoMySQL and AccountDaoOracle may both implement AccountDao)

    3. A Class may implement multiple interfaces. Java does not allow multiple inheritance of concrete classes.

    4. Abstracts implementation details. Interfaces may include only public API methods, hiding implementation details. Benefits include a cleanly documented public API and well documented contracts.

    5. Used heavily by modern dependency injection frameworks, such as http://www.springframework.org/.

    6. In Java, interfaces can be used to create dynamic proxies - http://java.sun.com/j2se/1.5.0/docs/api/java/lang/reflect/Proxy.html. This can be used very effectively with frameworks such as Spring to perform Aspect Oriented Programming. Aspects can add very useful functionality to Classes without directly adding java code to those classes. Examples of this functionality include logging, auditing, performance monitoring, transaction demarcation, etc. http://static.springframework.org/spring/docs/2.5.x/reference/aop.html.

    7. Mock implementations, unit testing - When dependent classes are implementations of interfaces, mock classes can be written that also implement those interfaces. The mock classes can be used to facilitate unit testing.

    0 讨论(0)
  • 2020-12-05 01:10

    Programming to an interface means respecting the "contract" created by using that interface. And so if your IPoweredByMotor interface has a start() method, future classes that implement the interface, be they MotorizedWheelChair, Automobile, or SmoothieMaker, in implementing the methods of that interface, add flexibility to your system, because one piece of code can start the motor of many different types of things, because all that one piece of code needs to know is that they respond to start(). It doesn't matter how they start, just that they must start.

    0 讨论(0)
提交回复
热议问题