문제

Ok. I have some questions regarding to some aspects of OO, and library design.

  1. Should a library be self-sufficient? Eg., can it use an external Dependency Injection framework, or should it implement it own, in a more lightweight manner?

  2. How does the Liskov's Substitution Principle fits on polymorphism, where you don't know the behavior of a method or class? You just expect it to work like it should?

  3. On the source code part, is a bad habit keep the interfaces on a separated folder (eg., /interfaces) from their implementation?

  4. It's also a bad habit delimit generic types (where T : type) on interfaces rather than just in their implementations? (this one I don't think so, but it's just to confirm it).

  5. Is an interface favorable over a abstract class when the object relationship is both, "can do" and "is a", and there's no need of a default implementation for methods and etc?

That's it. Thanks for your time =)

도움이 되었습니까?

해결책

1. Should a library be self-sufficient?

Libraries can surely use, for example, Dependency Injection frameworks. However, it is debatable whether you want to force the DI framework upon the user of the library.

2. How does the Liskov's Substitution Principle fit on polymorphism?

Liskov's Substitution Principle is about being able to interchange one implementation with another (substitution), and regardless of the behavior, there should be no errors when the user adheres to the contract. For example, swapping a CanonPrinter for an EpsonPrinter should still allow you to print if you use just the methods from the Printer class. Calling something Printer but having a particular implementation (Canon, Epson) underlying it is polymorphism.

3. Is it a bad habit to keep the interfaces in a folder separate from their implementation?

It is a personal preference whether you'll want to keep interfaces apart from their implementation. I don't do it; I don't even have an interface for each implementation. I think it only makes sense to have an interface for an implementation if it adds value to your project.

4. Is it also a bad habit to limit generic types on interfaces rather than just in their implementations?

If you think a generic type should be limited to a particular type, then you should do that where it makes sense (so, this may be on the interface). For example, having a IPrintableDocument<TPrinter> and not limiting TPrinter to Printer objects would not make sense, so I'd do it.

5. Is an interface favorable over an abstract class when the object relationship is both "can do" and "is a"?

Indeed, most people use abstract classes for is a and interfaces for can do relations. The reason: a class can inherit from only one base class but from multiple interfaces. In essence, this means that a class can be only one thing (a Employee is a Person) but can do multiple (it might ICopyDocuments, IWalkAbout, IMakeCoffee). Whatever you do when an interface is both depends on your preferences.


Most of your questions have to do with the contract of a class (or interface): the contract specifies what a user (another class, someone else's code) can do and cannot do with the class and its members, and it specifies what the user may expect the class will do and will not do.

For example: the user can only pass objects as arguments to a method when they match the parameter type. The class will only return objects as return values that match the return type. A user can only call members defined in the class, and the class will make sure those methods behave according to the contract (i.e. not throwing errors, no side effects, never returning null).

Generic type constraints are also part of the contract. I even consider the documentation part of the contract: when it states that a method will never throw an exception, then no implementation should ever throw an exception. There is no syntax or compiler rule enforcing it, but it is part of the contract. Some parts of the contract are enforced by the compiler or the runtime, others are not.

When a class or interface has a particular contract, then any subclass or implementation can be substituted for it and it will still work when that subclass or implementation adheres to the contract. If it adheres to the contract, then that's Liskov's Substitution Principle (LSP). And it is easily to deviate from it, and many programmers do. Sometimes you have no choice.

A clear example of a violation of the LSP in the .NET Framework is the ReadOnlyCollection<T> class. It implements the IList<T> interface but has no actual implementation for many of its methods. So if you pass a user expecting a IList<T> a ReadOnlyCollection<T> and the user tries to call list.Add, then an exception will be thrown. So, you cannot always substitute IList<T> for ReadOnlyCollection<T>, so it violates the LSP.

다른 팁

I'll try to answer these in order:

  1. It depends. Taking on an external dependency potentially limits the usefulness and scope of your library, as it will only be usable in situations where that dependency is available. This may be fine, however, and preferential to trying to work around the dependency. If you know the users of your library will always be using a specific DI framework, I'd build the library around it instead of implementing something that will need "adapting" to work.

  2. "where you don't know the behavior of a method or class?" You should know the contract of any types you are using. LSP basically says that, no matter what concrete type you get, the base class/interface contract will always be valid. This means you can just "use the type" and expect it to work.

  3. No - but this is a private implementation detail. It is typically a bad idea to separate out the namespaces, however, and it's often nice to keep the source code tree matching the namespaces.

  4. No. If an interface is really intended to only be used with a specific type, it should have the contraints on the interface.

  5. In general, a single contract should only provide one aspect of the relationship. If the contract suggests "is-a" and "can-do", it likely should be split into two contracts, perhaps an abstract class which implements the "can-do" interface.

I will take a stab at 5.

The only difference between an abstract class and an interface is that an abstract class can have class variables. I would choose an interface if the abstract class is only composed of viratual methods.

On #1 - http://jeviathon.com/2012/03/05/roll-your-own-syndrome/

Basically, the point is, why "roll your own" xyz framework when someone has likely already created robust, tested and supported software which does the same thing.

Spend your time researching picking the "right" external libraries and frameworks to use, not reinventing the wheel.

  1. "It depends". But there's little value in reinventing your own wheel, especially in so populated (and hard to implement) areas as DI and ORM. And especially when you can (a) bin-deploy your dependencies or (b) specify your dependencies as nuget packages, if your library is nuget package (as it of course should be)

  2. LSP has little relation to polymorphism. It states that class (or method) should behave according to contract, and that is, in fact, the only thing that you do know about implementor's behavior.

  3. Folders should correspond to namespaces. Do you put interfaces in separate namespace? I do not, and I don't think anybody should. And when I have interface with only one implementation (e.g., unit-testing-induced), I prefer to keep in in one file with its implementation.

  4. No, it's not. Why should it be?

  5. If there's no need for default implementation, got for an interface. Interface is easier to mock and also easier to implement, because it doesn't mess with your implementation's inheritance. In fact, you can even implement several interfaces on one class, if that somehow highlights your intent (for example, if you have specific service dealing with security, it is not uncommon for it to implement both IAuthentication and IAuthorization). Lately I choose abstract classes only when there's a clear need for them.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top