I've inherited a Visual Studio Solution that contains numerous circular references between Projects.
Is there ever a situation where this is remotely acceptable?
Just trying to confirm my suspicion that this application is designed horribly. Thanks in advance.
I once read a column where they compared 3 kinds of models: the Spaghetti model, the Lasagna model and the Ravioli model.
In the Spaghetti model, all the code is interlinked with each other, there is no clear structure. It's horrible, and we probably can all agree on that.
In the Lasagna model, the code is divided in different layers, and only a higher-level layer can access a lower-level layer, never the other way around.
In the Ravioli model, code is grouped in smaller modules. Every module only exposes what needs to be exposed, but every module can still access every other module.
About 10 years ago, it seemed to me that the Ravioli model is better than the Lasagna model. After all, in Java you also have Java modules which can easily call each other (and I had the impression that there was no real structure between all the different Java modules). To me, the Lasagna model seemed the result of non-object-oriented old code, while the Ravioli model seemed more modern, more object-oriented.
Nowadays, I tend to go back to the Lasagna model, but with a Ravioli model built-in. This is:
The application is built using different layers, like in the Lasagna model
But within the layers, the code is still split up in between different modules that can access each other, like in a Ravioli model.
Certain circular references may be difficult or impossible to remove. An example is the following:
Suppose you have a FileWriter class in your application and a Debug class. The Debug class will need the FileWriter class since it needs to write files with debug information.
On the other hand, the FileWriter class may also want to use the Debug class.
Notice that the circular reference in this example may already lead to problems (the FileWriter class could call the Debug class while writing a line, but the Debug class uses the FileWriter class to write the debug information, result: stack overflow).
In this case, the problem could be easily solve by not using the FileWriter class in the Debug class, but to use the native iostreams (if you're developing in C++). In other cases, the problem might be much harder to solve.
Good software is designed in layers with clearly demarcated boundaries between them. ie: If you have a layer, you need to be able to clearly articulate what it does, why it's there, and what it depends upon. Circularities make this difficult to achieve, and should generally be removed. (Microsoft has spent lots of effort in Windows 7 to improve the layering of Windows, by removing Circularities.)
Just trying to confirm my suspicion that this application is designed horribly.
This would definitely support that theory, but IMO, you'd need more than just a few circular references to draw that conclusion.
To answer your original question: yes, circular references can be helpful at times. Mutually recursive functions are a good example of that kind of thing. However... that's a circular reference safely hidden away within a module. For inter-module dependancies, a circular dependency usually means that your code isn't correctly split out across the modules, and this can require some significant refactoring to fix. (Including adding new sorts of abstactions to bridge the gap, etc.)
Concretely I'd advise using NDepend to detect and avoid dependency cycles.
Excerpt from the article (I wrote): Control component dependencies to gain clean architecture
Dependency cycles between components lead to what is commonly called spaghetti code or tangled code. If component A depends on B that depends on C that depends on A, the component A can’t be developed and tested independently of B and C. A, B and C form an indivisible unit, a kind of super-component. This super-component has a higher cost than the sum of the cost over A, B and C because of the diseconomy of scale phenomenon (well documented in Software Estimation: Demystifying the Black Art by Steve McConnell). Basically, this holds that the cost of developing an indivisible piece of code increases exponentially.
This suggests that developing and maintaining 1,000 LOC (Lines Of Code) will likely cost three or four times more than developing and maintaining 500 LOC, unless it can be split in two independent lumps of 500 LOC each. Hence the comparison with spaghetti that describes tangled code that can’t be maintained. In order to rationalize architecture, one must ensure that there are no dependency cycles between components, but also check that the size of each component is acceptable (500 to 1000 LOC).
Circular project references are a sign of bad design and should be removed if at all possible.
The only justification I can think of for keeping a circular reference would be a back compat issue. Even then it seems like it could be fixed with the use of type forwarders and another assembly
Related
I work on a project that leans on an app-level provide for global state management. At first, the provided object was small, but it has grown with the project.
How much does app performance suffer for injecting this object where it's data is needed? Short of implementing a global state management tool, like Pinia, is it worthwhile to decompose the large object we're providing into individual objects to provide "chunks" of data where its needed?
Since it's global, I guess it's quite big memory wise. Take a few hours to setup Pinia, will be faster/easier since it will come with only the parts you need.
We can't give you an exact number to your solution since it depends of a lot of things mostly.
PS: it can also be totally irrelevant and coming from another part of your code tbh.
Closed. This question is off-topic. It is not currently accepting answers.
Want to improve this question? Update the question so it's on-topic for Stack Overflow.
Closed 10 years ago.
Improve this question
I am new in web development, and read some wiki and discussions about MVC. However, the more I read, the more confusion I have about its design purpose.
I just want to know why is this design pattern invented? And what problem is it used to solve?
Thanks in advance.
The goal of the MVC paradigm is in essence to ensure a form of separation of code. The problem that often arise when developing code is that the code is written in a succession, where each part follows another and where each part is directly dependent upon what the other parts are doing.
When working with a large project, maintaining and further developing the code can quickly become an issue. You could therefore argue, in a simplified manner, that what the MVC paradigm tries to do is to ensure that you separate business logic (e.g. the code that performs) from the presentation logic (the code that shows the results). But those two parts need to communicate with each other, which is what the controller is responsible for.
This allows for a clear structure of code where the different parts are more decoupled, meaning less dependent upon each other.
The separation also means that you work in a much more modular way, where each part interacts with the others through an interface (some defined functions and variables that are used to call upon other parts) so that you can change the underlying functionality without having the change other parts of your code, as long as your interface remains the same.
So the problem it tries to solve is to avoid having a code base that is so entangled that you can't change or add anything without breaking the code, meaning you have to modify the code in all sorts of places beyond where you made your original changes.
To some degree it's a solution in search of a problem.
As a rather ancient programmer I'm well aware of the benefits of "separation of concerns", but (in my not-so-humble opinion) MVC doesn't do this very well, especially when implemented "cook-book" fashion. Very often it just leads to a proliferation of modules, with three separate modules for every function, and no common code or common theme to tie things together and accomplish the real goal: minimize complexity and maximize reliability/maintainability.
"Classical" MVC is especially inappropriate in your typical phone GUI app, where, eg, management of a database table may be intimately connected to management of a corresponding table view. Spreading the logic out among three different modules only makes things more complicated and harder to maintain.
What does often work well is to think about your data and understand what sorts of updates and queries will be required, then build a "wrapper" for the database (or whatever data storage you use), to "abstract" it and minimize the interactions between the DB and the rest of the system. But planning this is hard, and a significant amount of trial and error is often required -- definitely not cook-book.
Similarly you can sometimes abstract other areas, but abstracting, say, a GUI interface is often too difficult to be worthwhile -- don't just write "wrappers" to say you did it.
Keep in mind that the authors of databases, GUI systems, app flow control mechanisms, etc, have already put considerable effort (sometimes too much) into abstracting those interfaces, so your further "abstraction" is often little more than an extra layer of calls (especially if you take the cook-book approach).
Model view controller was created to separate concerns in code instead of crating a hodge podge all in a single blob. (Spaghetti code) the view code is merely presentation logic, the model is your objects representing your domain and the controller handle negotiating business logic and integrations to services on the backend.
I'm reading Randall Hyde's Write great code (volume 2) and I found this:
[...] it’s not good programming practice to create monolithic
applications, where all the source code appears in one source file (or is processed by a single compilation) [...]
I was wondering, why is this so bad?
Thanks everyone for your answers, I really wish to accept more of them, but I've chosen the most synthetic, so that who reads this question finds immediately the essentials.
Thanks guys ;)
The main reason, more important in my opinion than the sheer size of the file in question, is the lack of modularity. Software becomes easy to maintain if you have it broken down into small pieces whose interactions with each other are through a few well defined places, such as a few public methods or a published API. If you put everything in one big file, the tendency is to have everything dependent on the internals of everything else, which leads to maintenance nightmares.
Early in my career, I worked on a Geographic Information System that consisted of over a million lines of C. The only thing that made it maintainable, the only thing that made it work in the first place is that we had a sharp dividing line between everything "above" and everything "below". The "above" code implemented user interfaces, application specific processing, etc, and everything "below' implemented the spatial database. And the dividing line was a published API. If you were working "above", you didn't need to know how the "below" code worked, as long as it followed the published API. If you were working "below", you didn't care how your code was being used, as long as you implemented the published API. At one point, we even replaced a huge chunk of the "below" side that had stored stuff in proprietary files with tables in SQL databases, and the "above" code didn't have to know or care.
Because everything gets so crammed.
If you have separate files for separate things then you'll be able to find and edit it much faster.
Also, if there's an error, you can locate it more easily.
Because you quickly get (tens of) thousands of lines of unmaintainable code.
Impossible to read, impossible to maintain, impossible to extend.
Also, it's good for team work, since there are a lot of small files which can be edited in parallel by different developers.
Some reasons:
It's difficult to understand something that's all jammed together like that. Breaking
the logic into functions makes it easier to follow.
Factoring the logic into discrete components allows you to re-use those components elsewhere. Sometimes "elsewhere" means "elsewhere in the same program."
You cannot possibly unit-test something that's built in a monolithic fashion. Breaking
a huge function (or program) into pieces allows you to test those pieces individually.
While I understand that HTML and CSS aren't 'programming languages,' I suppose you could look at it in terms of having all of the .css, .js and .html all on one page: It makes it difficult to recycle or debug code.
Including all the source code in a single file makes it hard to manage code. The better approach should be to divide your program in separate modules. Each module should have its own source file and all the modules should be linked finally to create the executable. This helps a lot in maintaining code and makes it easy to find module specific bugs.
Moreover, if you have 2 or more people working simultaneously on the same project, there is a high probability that you will be wasting lots of your time in resolving code conflicts.
Also if you have to test individual components (or modules), you can do it easily without bothering about the other independent modules (if any).
Because decomposition is a computer science fundamental: Solve large problems by decomposing them into smaller pieces. Not only are the smaller problems more manageable, but they make it easier to keep a picture of the entire problem in your head.
When you talk about a monolithic program in a single file, all I can think of are the huge spaghetti code that found its way into COBOL and FORTRAN way back in the day. It encourages a cut and paste mentality that only gets worse with time.
Object-oriented languages are an attempt to help with this. OO languages decompose problems software components, with data and functions encapsulated together, that map to our mental models of how the world works.
Think about it in terms of the Single Responsibility Principle. If that one large file, or one monolithic application, or one "big something" is responsible for everything concerning that system, it's a catch-all bucket of functionality. That functionality should be separated into its discrete components for maintainability, re-usability, etc.
Programmers do a thousand line of codes right, if you do things on separate side. you can easily manage and locate your files.
Apart from all the above mentioned reasons, typically all the text from a compilation unit is included in final program. However if you split things up and it turns out all code in particular file is not getting used it will not be linked. Even if its getting linked it might be easy to turn it into a DLL should you want to decide using the functionality at run time. This facilitates dependency management, reduces build times by compiling only the modified source file leading to greater maintainability and productivity.
From some really bad experience I can tell you that it's unreadable. Function after function of code, you can never understand what the programmer meant or how to find your way out of it.
You cannot even scroll in the file because a little movement of the scroll bar scrolls two pages of code.
Right now I'm rewriting an entire application because the original programmers thought it a great idea to create 3000-line code files. It just cannot be maintained. And of course it's totally untestable.
I think you have never worked in big company that has 6000 lines of code in one file... and every couple thousand lines you see comments like /** Do not modify this block, it's critical */ and you think the bug assigned to you is coming from that block. And your manager says, "yeah look into that file, it's somewhere in it."
And the code breaks all those beautiful OOP concepts, has dirty switches. And like fifty contributors.
Lucky you.
I am struggling to see the real-world benefits of loosely coupled code. Why spend so much effort making something flexible to work with a variety of other objects? If you know what you need to achieve, why not code specifically for that purpose?
To me, this is similar to creating untyped variables: it makes it very flexible, but opens itself to problems because perhaps an unexpected value is passed in. It also makes it harder to read, because you do not explicitly know what is being passed in.
Yet I feel like strongly typed is encouraged, but loosely coupling is bad.
EDIT: I feel either my interpretation of loose coupling is off or others are reading it the wrong way.
Strong coupling to me is when a class references a concrete instance of another class. Loose coupling is when a class references an interface that another class can implement.
My question then is why not specifically call a concrete instance/definition of a class? I analogize that to specifically defining the variable type you need.
I've been doing some reading on Dependency Injection, and they seem to make it out as fact that loose coupling better design.
First of all, you're comparing apples to oranges, so let me try to explain this from two perspectives. Typing refers to how operations on values/variables are performed and if they are allowed. Coupling, as opposed to cohesion, refers to the architecture of a piece (or several pieces) of software. The two aren't directly related at all.
Strong vs Weak Typing
A strongly typed language is (usually) a good thing because behavior is well defined. Take these two examples, from Wikipedia:
Weak typing:
a = 2
b = '2'
concatenate(a, b) # Returns '22'
add(a, b) # Returns 4
The above can be slightly confusing and not-so-well-defined because some languages may use the ASCII (maybe hex, maybe octal, etc) numerical values for addition or concatenation, so there's a lot of room open for mistakes. Also, it's hard to see if a is originally an integer or a string (this may be important, but the language doesn't really care).
Strongly typed:
a = 2
b = '2'
#concatenate(a, b) # Type Error
#add(a, b) # Type Error
concatenate(str(a), b) # Returns '22'
add(a, int(b)) # Returns 4
As you can see here, everything is more explicit, you know what variables are and also when you're changing the types of any variables.
Wikipedia says:
The advantage claimed of weak typing
is that it requires less effort on the
part of the programmer than, because
the compiler or interpreter implicitly
performs certain kinds of conversions.
However, one claimed disadvantage is
that weakly typed programming systems
catch fewer errors at compile time and
some of these might still remain after
testing has been completed. Two
commonly used languages that support
many kinds of implicit conversion are
C and C++, and it is sometimes claimed
that these are weakly typed languages.
However, others argue that these
languages place enough restrictions on
how operands of different types can be
mixed, that the two should be regarded
as strongly typed languages.
Strong vs weak typing both have their advantages and disadvantages and neither is good or bad. It's important to understand the differences and similarities.
Loose vs Tight Coupling
Straight from Wikipedia:
In computer science, coupling or
dependency is the degree to which each
program module relies on each one of
the other modules.
Coupling is usually contrasted with
cohesion. Low coupling often
correlates with high cohesion, and
vice versa. The software quality
metrics of coupling and cohesion were
invented by Larry Constantine, an
original developer of Structured
Design who was also an early proponent
of these concepts (see also SSADM).
Low coupling is often a sign of a
well-structured computer system and a
good design, and when combined with
high cohesion, supports the general
goals of high readability and
maintainability.
In short, low coupling is a sign of very tight, readable and maintainable code. High coupling is preferred when dealing with massive APIs or large projects where different parts interact to form a whole. Neither is good or bad. Some projects should be tightly coupled, i.e. an embedded operating system. Others should be loosely coupled, i.e. a website CMS.
Hopefully I've shed some light here :)
The question is right to point out that weak/dynamic typing is indeed a logical extension of the concept of loose coupling, and it is inconsistent for programmers to favor one but not the other.
Loose coupling has become something of a buzzword, with many programmers unnecessarily implementing interfaces and dependency injection patterns -- or, more often than not, their own garbled versions of these patterns -- based on the possibility of some amorphous future change in requirements. There is no hiding the fact that this introduces extra complexity and makes code less maintainable for future developers. The only benefit is if this anticipatory loose coupling happens to make a future change in requirements easier to implement, or promote code reuse. Often, however, requirements changes involve enough layers of the system, from UI down to storage, that the loose coupling doesn't improve the robustness of the design at all, and makes certain types of trivial changes more tedious.
You're right that loose coupling is almost universally considered "good" in programming. To understand why, let's look at one definition of tight coupling:
You say that A is tightly coupled to B if A must change just because B changed.
This is a scale that goes from "completely decoupled" (even if B disappeared, A would stay the same) to "loosely coupled" (certain changes to B might affect A, but most evolutionary changes wouldn't), to "very tightly coupled" (most changes to B would deeply affect A).
In OOP we use a lot of techniques to get less coupling - for example, encapsulation helps decouple client code from the internal details of a class. Also, if you depend on an interface then you don't generally have to worry as much about changes to concrete classes that implement the interface.
On a side note, you're right that typing and coupling are related. In particular, stronger and more static typing tend to increase coupling. For example, in dynamic languages you can sometimes substitute a string for an array, based on the notion that a string can be seen as an array of characters. In Java you can't, because arrays and strings are unrelated. This means that if B used to return an array and now returns a string, it's guaranteed to break its clients (just one simple contrived example, but you can come up with many more that are both more complex and more compelling). So, stronger typing and more static typing are both trade-offs. While stronger typing is generally considered good, favouring static versus dynamic typing is largely a matter of context and personal tastes: try setting up a debate between Python programmers and Java programmers if you want a good fight.
So finally we can go back to your original question: why is loose coupling generally considered good? Because of unforeseen changes. When you write the system, you cannot possibly know which directions it will eventually evolve in two months, or maybe two hours. This happens both because requirements change over time, and because you don't generally understand the system completely until after you've written it. If your entire system is very tightly coupled (a situation that's sometimes referred to as "the Big Ball of Mud"), then any change in every part of the system will eventually ripple through every other part of the system (the definition of "very tight coupling"). This makes for very inflexible systems that eventually crystallize into a rigid, unmaintanable blob. If you had 100% foresight the moment you start working on a system, then you wouldn't need to decouple.
On the other hand, as you observe, decoupling has a cost because it adds complexity. Simpler systems are easier to change, so the challenge for a programmer is striking a balance between simple and flexible. Tight coupling often (not always) makes a system simpler at the cost of making it more rigid. Most developers underestimate future needs for changes, so the common heuristic is to make the system less coupled than you're tempted to, as long as this doesn't make it overly complex.
Strongly typed is good because it prevents hard to find bugs by throwing compile-time errors rather than run-time errors.
Tightly coupled code is bad because when you think you "know what you need to achieve", you are often wrong, or you don't know everything you need to know yet.
i.e. you might later find out that something you've already done could be used in another part of your code. Then maybe you decide to tightly couple 2 different versions of the same code. Then later you have to make a slight change in a business rule and you have to alter 2 different sets of tightly coupled code, and maybe you will get them both correct, which at best will take you twice as long... or at worst you will introduce a bug in one, but not in the other, and it goes undetected for a while, and then you find yourself in a real pickle.
Or maybe your business is growing much faster than you expected, and you need to offload some database components to a load-balancing system, so now you have to re-engineer everything that is tightly coupled to the existing database system to use the new system.
In a nutshell, loose coupling makes for software that is much easier to scale, maintain, and adapt to ever-changing conditions and requirements.
EDIT: I feel either my interpretation
of loose coupling is off or others are
reading it the wrong way. Strong
coupling to me is when a class
references a concrete instance of
another class. Loose coupling is when
a class references an interface that
another class can implement.
My question then is why not
specifically call a concrete
instance/definition of a class? I
analogize that to specifically
defining the variable type you need.
I've been doing some reading on
Dependency Injection, and they seem to
make it out as fact that loose
coupling better design.
I'm not really sure what your confusion is here. Let's say for instance that you have an application that makes heavy use of a database. You have 100 different parts of your application that need to make database queries. Now, you could use MySQL++ in 100 different locations, or you can create a separate interface that calls MySQL++, and reference that interface in 100 different places.
Now your customer says that he wants to use SQL Server instead of MySQL.
Which scenario do you think is going to be easier to adapt? Rewriting the code in 100 different places, or rewriting the code in 1 place?
Okay... now you say that maybe rewriting it in 100 different places isn't THAT bad.
So... now your customer says that he needs to use MySQL in some locations, and SQL Server in other locations, and Oracle in yet other locations.
Now what do you do?
In a loosely coupled world, you can have 3 separate database components that all share the same interface with different implementations. In a tightly coupled world, you'd have 100 sets of switch statements strewn with 3 different levels of complexity.
If you know what you need to achieve, why not code specifically for that purpose.
Short answer: You almost never know exactly what you need to achieve. Requirements change, and if your code is loosely coupled in the first place, it will be less of a nightmare to adapt.
Yet I feel like strongly typed is encouraged, but loosely coupling is bad.
I don't think it is fair to say that strong typing is good or encouraged. Certainly lots of people prefer strongly typed languages because it comes with compile-time checking. But plenty of people would say that weak typing is good. It sounds like since you've heard "strong" is good, how can "loose" be good too. The merits of a language's typing system isn't even in the realm of a similar concept as class design.
Side note: don't confuse strong and static typing
strong typing will help reduce errors while typically aiding performance. the more information the code-generation tools can gather about acceptable value ranges for variables, the more these tools can do to generate fast code.
when combined with type inference and feature's like traits (perl6 and others) or type classes (haskell), strongly typed code can continue to be compact and elegant.
I think that tight/loose coupling (to me: Interface declaration and assignment of an object instance) is related to the Liskov Principle. Using loose coupling enables some of the advantages of the Liskov Principle.
However, as soon as instanceof, cast or copying operations are executed, the usage of loose coupling starts being questionable. Furthermore, for local variables withing a method or block, it is non-sense.
If any modification done in our function, which is in a derived class, will change the code in the base abstract class, then this shows the full dependency and it means this is tight coupled.
If we don't write or recompile the code again then it showes the less dependency, hence it is loose coupled.
In day to day programs I wouldn't even bother thinking about the possible performance hit for coding against interfaces rather than implementations. The advantages largely outweigh the cost. So please no generic advice on good OOP.
Nevertheless in this post, the designer of the XNA (game) platform gives as his main argument to not have designed his framework's core classes against an interface that it would imply a performance hit. Seeing it is in the context of a game development where every fps possibly counts, I think it is a valid question to ask yourself.
Does anybody have any stats on that? I don't see a good way to test/measure this as don't know what implications I should bear in mind with such a game (graphics) object.
Coding to an interface is always going to be easier, simply because interfaces, if done right, are much simpler. Its palpably easier to write a correct program using an interface.
And as the old maxim goes, its easier to make a correct program run fast than to make a fast program run correctly.
So program to the interface, get everything working and then do some profiling to help you meet whatever performance requirements you may have.
What Things Cost in Managed Code
"There does not appear to be a significant difference in the raw cost of a static call, instance call, virtual call, or interface call."
It depends on how much of your code gets inlined or not at compile time, which can increase performance ~5x.
It also takes longer to code to interfaces, because you have to code the contract(interface) and then the concrete implementation.
But doing things the right way always takes longer.
First I'd say that the common conception is that programmers time is usually more important, and working against implementation will probably force much more work when the implementation changes.
Second with proper compiler/Jit I would assume that working with interface takes a ridiculously small amount of extra time compared to working against the implementation itself.
Moreover, techniques like templates can remove the interface code from running.
Third to quote Knuth : "We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil."
So I'd suggest coding well first, and only if you are sure that there is a problem with the Interface, only then I would consider changing.
Also I would assume that if this performance hit was true, most games wouldn't have used an OOP approach with C++, but this is not the case, this Article elaborates a bit about it.
It's hard to talk about tests in a general form, naturally a bad program may spend a lot of time on bad interfaces, but I doubt if this is true for all programs, so you really should look at each particular program.
Interfaces generally imply a few hits to performance (this however may change depending on the language/runtime used):
Interface methods are usually implemented via a virtual call by the compiler. As another user points out, these can not be inlined by the compiler so you lose that potential gain. Additionally, they add a few instructions (jumps and memory access) at a minimum to get the proper PC in the code segment.
Interfaces, in a number of languages, also imply a graph and require a DAG (directed acyclic graph) to properly manage memory. In various languages/runtimes you can actually get a memory 'leak' in the managed environment by having a cyclic graph. This imposes great stress (obviously) on the garbage collector/memory in the system. Watch out for cyclic graphs!
Some languages use a COM style interface as their underlying interface, automatically calling AddRef/Release whenever the interface is assigned to a local, or passed by value to a function (used for life cycle management). These AddRef/Release calls can add up and be quite costly. Some languages have accounted for this and may allow you to pass an interface as 'const' which will not generate the AddRef/Release pair automatically cutting down on these calls.
Here is a small example of a cyclic graph where 2 interfaces reference each other and neither will automatically be collected as their refcounts will always be greater than 1.
interface Parent {
Child c;
}
interface Child {
Parent p;
}
function createGraph() {
...
Parent p = ParentFactory::CreateParent();
Child c = ChildFactory::CreateChild();
p.c = c;
c.p = p;
... // do stuff here
// p has a reference to c and c has a reference to p.
// When the function goes out of scope and attempts to clean up the locals
// it will note that p has a refcount of 1 and c has a refcount of 1 so neither
// can be cleaned up (of course, this is depending on the language/runtime and
// if DAGS are allowed for interfaces). If you were to set c.p = null or
// p.c = null then the 2 interfaces will be released when the scope is cleaned up.
}
I think object lifetime and the number of instances you're creating will provide a coarse-grain answer.
If you're talking about something which will have thousands of instances, with short lifetimes, I would guess that's probably better done with a struct rather than a class, let alone a class implementing an interface.
For something more component-like, with low numbers of instances and moderate-to-long lifetime, I can't imagine it's going to make much difference.
IMO yes, but for a fundamental design reason far more subtle and complex than virtual dispatch or COM-like interface queries or object metadata required for runtime type information or anything like that. There is overhead associated with all of that but it depends a lot on the language and compiler(s) used, and also depends on whether the optimizer can eliminate such overhead at compile-time or link-time. Yet in my opinion there's a broader conceptual reason why coding to an interface implies (not guarantees) a performance hit:
Coding to an interface implies that there is a barrier between you and
the concrete data/memory you want to access and transform.
This is the primary reason I see. As a very simple example, let's say you have an abstract image interface. It fully abstracts away its concrete details like its pixel format. The problem here is that often the most efficient image operations need those concrete details. We can't implement our custom image filter with efficient SIMD instructions, for example, if we had to getPixel one at a time and setPixel one at a time and while oblivious to the underlying pixel format.
Of course the abstract image could try to provide all these operations, and those operations could be implemented very efficiently since they have access to the private, internal details of the concrete image which implements that interface, but that only holds up as long as the image interface provides everything the client would ever want to do with an image.
Often at some point an interface cannot hope to provide every function imaginable to the entire world, and so such interfaces, when faced with performance-critical concerns while simultaneously needing to fulfill a wide range of needs, will often leak their concrete details. The abstract image might still provide, say, a pointer to its underlying pixels through a pixels() method which largely defeats a lot of the purpose of coding to an interface, but often becomes a necessity in the most performance-critical areas.
Just in general a lot of the most efficient code often has to be written against very concrete details at some level, like code written specifically for single-precision floating-point, code written specifically for 32-bit RGBA images, code written specifically for GPU, specifically for AVX-512, specifically for mobile hardware, etc. So there's a fundamental barrier, at least with the tools we have so far, where we cannot abstract that all away and just code to an interface without an implied penalty.
Of course our lives would become so much easier if we could just write code, oblivious to all such concrete details like whether we're dealing with 32-bit SPFP or 64-bit DPFP, whether we're writing shaders on a limited mobile device or a high-end desktop, and have all of it be the most competitively efficient code out there. But we're far from that stage. Our current tools still often require us to write our performance-critical code against concrete details.
And lastly this is kind of an issue of granularity. Naturally if we have to work with things on a pixel-by-pixel basis, then any attempts to abstract away concrete details of a pixel could lead to a major performance penalty. But if we're expressing things at the image level like, "alpha blend these two images together", that could be a very negligible cost even if there's virtual dispatch overhead and so forth. So as we work towards higher-level code, often any implied performance penalty of coding to an interface diminishes to a point of becoming completely trivial. But there's always that need for the low-level code which does do things like process things on a pixel-by-pixel basis, looping through millions of them many times per frame, and there the cost of coding to an interface can carry a pretty substantial penalty, if only because it's hiding the concrete details necessary to write the most efficient implementation.
In my personal opinion, all the really heavy lifting when it comes to graphics is passed on to the GPU anwyay. These frees up your CPU to do other things like program flow and logic. I am not sure if there is a performance hit when programming to an interface but thinking about the nature of games, they are not something that needs to be extendable. Maybe certain classes but on the whole I wouldn't think that a game needs to programmed with extensibility in mind. So go ahead, code the implementation.
it would imply a performance hit
The designer should be able to prove his opinion.