3

I have some questions about the affects of using concrete classes and interfaces.

  1. Say some chunk of code (call it chunkCode) uses concrete class A. Would I have to re-compile chunkCode if:

    1. I add some new public methods to A? If so, isn't that a bit stange? After all I still provide the interface chunkCode relies on. (Or do I have to re-compile because chunkCode may never know otherwise that this is true and I haven't omitted some API)
    2. I add some new private methods to A?
    3. I add a new public field to A?
    4. I add a new private field to A?
  2. Factory Design Pattern: The main code doesn't care what the concrete type of the object is. It relies only on the API. But what would you do if there are few methods which are relevant to only one concrete type? This type implements the interface but adds some more public methods? Would you use some if (A is type1) statements (or the like) the main code?

Thanks for any clarification

Elad Benda
  • 30,720
  • 75
  • 234
  • 415
  • Could you add some example code? Generally if an interface doesn't change you won't need to recompile, but if you are working against concrete classes you will. – davecoulter Aug 27 '11 at 00:27
  • 1
    ha, everybody is editing this post. I liked the bulleted list better ... – celavek Aug 27 '11 at 00:35

3 Answers3

4

1) Compiling is not an activity in OO. It is a detail of specific OO implementations. If you want an answer for a specific implementation (e.g. Java), then you need to clarify.

In general, some would say that adding to an interface is not considered a breaking change, wheras others say you cannot change an interface once it is published, and you have to create a new interface.

Edit: You specified C#, so check out this question regarding breaking changes in .Net. I don't want to do that answer a disservice, so I won't try to replicate it here.

2) People often hack their designs to do this, but it is a sign that you have a poor design.

Good alternatives:

  • Create a method in your interface that allows you to invoke the custom behavior, but not be required to know what that behavior is.

  • Create an additional interface (and a new factory) that supports the new methods. The new interface does not have to inherit the old interface, but it can if it makes sense (if an is-a relationship can be expressed between the interfaces).

  • If your language supports it, use the Abstract Factory pattern, and take advantage of Covariant Return Types in the concrete factory. If you need a specific derived type, accept a concrete factory instead of an abstract one.

Bad alternatives (anti-patterns):

  • Adding a method to the interface that does nothing in other derived classed.

  • Throwing an exception in a method that doesn't make sense for your derived class.

  • Adding query methods to the interface that tell the user if they can call a certain method.

Unless the method name is generic enough that the user wouldn't expect it to do anything (e.g. DoExtraProcessing), then adding a method that is no-op in most derived classes breaks the contract defined by that interface.

E.g.: Someone invoking bird.Fly() would expect it to actually do something. We know that chickens can't fly. So either a Chicken isn't a Bird, or Birds don't Fly.

Adding query methods is a poor work-around for this. E.g. Adding a boolean CanFly() method or property in your interface. So is throwing an exception. Neither of them get around the fact that the type simply isn't substitutable. Check out the Liskov Substitution Principle (LSP).

Community
  • 1
  • 1
Merlyn Morgan-Graham
  • 54,918
  • 14
  • 119
  • 174
  • One notable exception to the anti-patterns I listed above is the [Freezable Pattern](http://msdn.microsoft.com/en-us/library/ms602734.aspx). In this case you'd want to make sure your interface/base class you returned was freezable, and that the documentation for the factory stated that it always returned instances that were frozen or unfrozen. – Merlyn Morgan-Graham Aug 27 '11 at 01:19
  • 1) I refer to c#. Then who is right (regarding c#): 0verbose or celavek ? – Elad Benda Aug 27 '11 at 10:34
  • could you please elaberate on: "If your language supports it..abstract one." is it the same idea as: "Create an additional interface...between the interfaces)." – Elad Benda Aug 27 '11 at 10:45
  • @Elad: No, you don't create a new interface (for the type being constructed). You create a new factory interface, and derived from it. Then change the signature in the concrete factory to return the concrete type. This only works if your language supports covariant return types. C++ supports this, but I don't think C# does without some work-arounds (e.g. explicit interface implementation forwarding to a `new` method with a different return type). – Merlyn Morgan-Graham Aug 27 '11 at 20:30
  • @Elad: As for #1, I'll link to a much more complete question on .Net breaking changes – Merlyn Morgan-Graham Aug 27 '11 at 20:32
  • @Elad: If you're still confused, then definitely check out Covariant Return Types and the Abstract Factory pattern, possibly starting at those links I provided. All will become clear :) – Merlyn Morgan-Graham Aug 27 '11 at 20:36
0

For your first question the answer is NO for all your points. If it would be that way then backward compatibility would not make any sense. You have to recompile chunkCode only if you brake the API, that is remove some functionality that chunkCode is using, changing calling conventions, modifying number of parameters, these sort of things == breaking changes.

For the second I usually, but only if I really have to, use dynamic_cast in those situations.

Note my answer is valid in the context of C++;I just saw the question is language agnostic(kind of tired at this hour; I'll remove the answer if it offenses anybody).

celavek
  • 5,195
  • 5
  • 37
  • 68
  • How would you respond to 0verbose answer? "If chunkCode directly refers to A concrete class yes" – Elad Benda Aug 27 '11 at 10:27
  • 1
    @Elad Benda In the case of C++ I think my answer holds true. In the case of Java it also holds true - in Java you don't have to recompile even if you change your interface in the case of adding to the interface(see http://stackoverflow.com/questions/6780899/do-clients-have-to-recompile-after-changing-a-java-interface; the link provided in the accepted answer is a good read). Sometimes though, in edge cases, you need to indeed recompile see http://stackoverflow.com/questions/536971/do-i-have-to-recompile-my-application-when-i-upgrade-a-third-party-jar. – celavek Aug 27 '11 at 15:06
0

Question 1: Depends on what language you are talking about. Its always safer to recompile both languages though. Mostly because chuckCode does not know what actually exists inside A. Recompiling refreshes its memory. But it should work in Java without recompiling.

Question 2: No. The entire point of writing a Factory is to get rid of if(A is type1). These if statements are terrible from maintenance perspective.

Factory is designed to build objects of similar type. If you are having a situation where you are using this statement then that object is either not a similar type to rest of the classes. If you are sure it is of similar type and have similar interfaces. I would write an extra function in all the concrete base classes and implement it only on this one.

Ideally All these concrete classes should have a common abstract base class or a Interface to define what the API is. Nothing other than what is designed in this Interface should be expected to be called anywhere in the code unless you are writing functions that takes this specific class.

arunmur
  • 620
  • 4
  • 5
  • "Recompiling refreshes its memory. " Recompiling may mean to clean up the output from the last compilation and compile again, not refreshing memory.. – devoured elysium Aug 27 '11 at 01:16
  • I didnt mean the physical memory. I meant that it will re-establish the class and function locations. This is especially necessary if the interface is changed. You atleast get a compilation error. This might not be so true with Java but If you look at C++ the actual locations of the functions are stored in the code. – arunmur Aug 29 '11 at 11:40