I see 'Composition' as a general term referring to intentional design. Basically, planning the relationships/interactions of a programming to work in a consistent and predicable manner.
Depending on how you expect your code to be used, whether it's a publicly exposed API or a one-off you need to make choices about which parts you want to expose and how much effort you put into controlling access of parts. OOP in languages like C# and Java are great because you have finer grained control over the access of internal components through the use of access keywords (static, private, public, internal).
I prefer the saying, "APIs are like children, if you do a bad job early on you'll be forced to support them for life."
Basically, you want to strictly control access to your API because anything you make available, other people will eventually depend on. The more you expose, the more other programs will depend on later; making it impossible to make changes later without breaking code.
I see 'Components' as the APIs themselves and their external interfaces. Whether the boundary is based on libraries (ie dynamically linked libraries), a network interface (ex REST, SOAP), or an multi-process architecture (IPC) it's necessary to define how the different parts interact.
The graph shown on the wikipedia article you linked is pretty useless because it doesn't show what data is shared between the parts.
The best way to go about doing this is, draw out all of the different components, and map all of the inputs/outputs between the different parts.
If you want to pull a Twitter feed to your website then you'd put one component for twitter, one for your website.
Then you discuss the relationship requirements:
- Do you want to display a list of recent posts?
- Do you want to be able to post to twitter remotely?
In the first case you're requesting and fetching data from twitter so the relationship is described as an output from the twitter API as an input to your application. The reverse is true for posting. You have a post created from your site being outputted to twitter.
The details of the implementation are abstracted away because it's too hard to think about everything at once.
A Component Diagram provides the 'bigger picture' view of the relationships. The amount of detail you want to provide depends on what level of detail you need to communicate those relationships.
Time to step down from the clouds. Composites actually describe the meat of the code. I'm talking about classes, methods, objects, functions, prototypes, etc... These are the structures that are built and built-on. The number of composites and complexity of their interactions is vast.
In an ideal world where developers have unlimited time to burn and actually like creating documentation, it would make sense to have 100% diagram coverage on the composites. In practice, composite diagrams are near useless because they're never in-sync with the codebase. Even when they are in-sync they're completely redundant and provide no practical application on their own.
Class diagram generators like the one in Visual Studio is great for navigation but I wouldn't consider its use as a composite diagram because it's missing the most important details (ie interface interrelationships).
If you need something outside of the code for users to chomp on, just use an auto documentation generation application and good quality code commenting to produce a Software User's Manual.
Every API ever created suffers from poor design of composites because assumptions about the function of an API tend to change over time. As an example, have you ever heard of YouTube? Would you believe if I told you that it was originally supposed to be a dating site? Google it.
Don't worry, it happens. If you're designing an API, you'll probably break somebody's code at some point. It's inevitable but there are steps you can take early on to minimize the damage.
You'll probably break you own code too. If you're making changes to mission/production critical code that's what tests are for. I would argue that any code that depends on an external API should have full test coverage wherever it touches those APIs but IRL the time investment of making that happen may just be too high.
Enough talk, lets build a fictional architecture to demonstrate
First, build an understanding of what you're trying to accomplish. As an example I'm going to create a fictional SuperStrings library.
It'll include a few different output methods to assist developers:
OK, so you may or may not have seen a ToString() method before but they're pretty common because ToString() is usually a method of the origin 'Object' class. It's also pretty much useless if you try to call it on any complex structure (ex, array, list, classes, etc). We want to revolutionize the world of strings.
I find the best way to start is to identify the components first. Our library isn't called SuperStringLoggingTextMessagingTweeter (unless this was a Microsoft implementation, then that would be totally acceptable). So we want to identify and abstract out the parts that we aren't actually going to code.
ToString() is the most basic primitive and has no dependencies. ToVerboseString() is the same. ToLog(), there are a lot of loggers already available and battle tested so it would be stupid to roll our own. ToFile(), also requires a library dependency. ToTextMessage(), now we're talking. ToTweet(), because we're so web 2.0-y.
So we know we need the following dependencies:
- A logging library
- A file access library
- A text message library
- A twitter API, or a library that implements one
The Component diagram would easily be explained as your library in the center with a 1-1 relationship with each of these dependencies. If your API is included in another application then there would be a 1-1 relationship with that app (or 1-many depending on how it's used).
The composites will be the methods described above as well as any related interfaces that implements one or more of them.
So you have the following static public methods:
// Displays only the value of the variable
// Displays the label, type, and value of a variable in an easy-to-grep format
// Outputs an 'INFO' log notice with the string in verbose format
// Appends the verbose string to a file
// Outputs the verbose string to a text message
// Creates a tweet containing the the verbose string
Interfaces, the sticky stuff that holds everything together
Finally, the fun part...
This is the stuff that you'll never see in a diagram because it's just too damn complex to draw. The 'creative' branch of development. The reason experienced developers laugh when a newbie claims, 'I know how to program' because they've finally managed to grasp basic programming fundamentals (ex, for loops, variables, arrays, etc).
When I say 'Interfaces' I'm not referring to the way they're implemented in some languages (ie, like C#). I'm referring to the more abstract concept of 'code that connects the constituent parts of functions/classes/methods into a complete model'.
It's completely possible (but possibly really difficult) to map all these out in your composite diagram once you know what you're trying to identify.
- Class Inheritance
- Multiple Inheritance
- Interfaces (the way C uses them)
These are the parts that turn a collection of one-off classes into a complicated web of interrelated class structures.
When you design interfaces, be very cautious about over-engineering. It's tempting to buy into the worshippers of abstracts and other such nonsense.
If you need a hard-fast rule for designing interfaces:
Go depth-first and abstract commonality only where it makes sense later
It's very easy to code yourself into a corner and lose the leverage to be creative. DRY (Don't Repeat Yourself) is a great principle but a poorly-designed or over-engineered class structure will require as many or more lines of boilerplate to 'break the mold' of a rigid structure as it would've taken to copy/paste a few sections of code.
The same thing applies to database normalization. Sure, normalizing a massive web of interrelated tables is impressive (and may provide some job security over the long-term); but, it'll probably make your head hurt every time you're forced to change it.
In short, weigh the consequences of your design and (hopefully) do what you can do prevent future suffering. Welcome to Software Development Ethics 101.
How you go about designing interfaces depends completely on the platform...
I would also argue that learning how to build interfaces between classes/prototypes/functions in multiple languages is the most valuable part of picking up new languages. I will attempt to explain a few uses but only as a taste of what's possible. To provide a complete list would be an undertaking equivalent to describing every possible medium an artist could use.
I figured I'd start with the most obvious. Pretty simple stuff, there's no functional code in an interface, it's just inherited as a contract of what your class should be capable of. The example code above could easily be implemented. Just create an interface, attach it to the SuperString (Ie common base) class, but don't implement the methods in the base class.
It's like an abstract base class but you can see it coming before the compiler throws a debug error, and it can be re-used anywhere.
Abstract Base Classes
If you're considering creating an ABC (Abstract Base Class) ::cringe::, dont...
Why do I hate ABCs so much? Because they're basically implicitly inherited abstracts. Interfaces, OTOH are explicitly inherited abstracts.
If you feel the need to practice the art of abstract classes/methods, do us all a favor and make them explicit (ie using an interface). Thank you.
I'm talking about public/private/internal. It's safe to ignore these altogether and make everything public if you're the only person who will ever touch the code. You only learn to appreciate them when it comes time to design an API.
When you're providing a module/dll any function/class/method that you make available will become a dependency of someone else's application at some point. You could literally create a test class that does nothing but demonstrate usage and some lazy guy out in the world will decide to use it in production-sensitive code just to save from re-writing it himself.
This is where the 'APIs are like children' saying applies. If you're draconian about locking down access early on, you'll give yourself more leverage to improve the internal implementations later.
Prototypes are the antithesis of 'Access Keywords' because they give everybody the power to change everything. Instead of sitting down and mapping out the object inheritance hierarchy before writing your classes (like you'd do in C#) you can just dump a different function/method into a function/class because in functional programming, functions are just another form of variable.
I nervously giggle like a schoolgirl because I feel like the OOP gods are looking down on me with disdain every time I use one of these. Ignoring preference and security, prototypes are the interface that make 90% of OOP source code look like boilerplate.
Basically, what it says. Create classes with sub-parts of your classes that can be inherited willy-nilly. What's the big deal? Circular references, that's what.
Without multiple-inheritance you get the mess that is Java/C# class design. I can't pretend to understand the extent of why Java/C# killed multiple inheritance but I'll assume that there are good reasons beside, "to save the developer from himself/herself."
What I do know, without multiple inheritance you'll need a lot more biolerplate code to generalize classes that share similar but in-exact implementations of code. With multiple inheritance, you'll be putting yourself in 'the danger zone;' but, some of us like that sort of thing.
Warning: Excessive 'Hand-Waviness' Ahead - If you don't understand what, "with a grain of salt" means, it's safe to ignore the rest.
In the world of academia (or at least what I can infer from the outside) professors are absolutely in love with the concept of abstract classes. Being self-taught (and learning C# as my first 'real' OOP language after PHP) I read a lot about classes before I ever started to gain an understanding of how they're implemented.
Of about 2k-3k pages and still lacking an understanding of OOP I finally came across a book that had practical examples I could whip up in Visual Studio and skipped right over all of the inheritance complexity and abstract class nonsense.
I'm one of those annoying people who can't connect how concept A maps to concept B until I see it in action so the depth-first approach that schools prefer makes me angry. One of the reasons I have a talent for coding is my ability to visualize massive complex structures and how they're interrelated in my head. It comes second-nature to me like breathing but mapping abstract concepts without any practical application is like chasing ghosts.
OOP is a very powerful programming paradigm. And the concept of mapping real-world objects directly to code just makes sense but the way it's taught explains why many people complete a degree and still don't know how to code.
I used to be an OOP evangelist for two reasons. First, OOP was the most useful paradigm I knew of to structure a program at the time. Second, I spent so much time bashing my head against the wall trying to figure it out that I was too emotionally invested to see a different perspective.
Nowadays, I find it a lot easier (and less stressful) to be open-minded. I can understand why rigid design facilities like UML diagrams were created. Architecting an application is hard and communicating an architecture to a group is even more difficult. Just don't make the mistake of idealizing a system where you can create a UML diagram and translate it directly to a working program. It has been done many times and it sucked every time.
The more rigid design structures like the ones presented in UML are, more creative leverage you sacrifice. Writing code is a creative art. That's why you hear graphic-designer-like expressions (ex, cleanliness, elegance, etc) being used to describe code.
Software doesn't 'need' design files because the source code itself is a design file. If the source code is way to complex to understand by taking a bredth-first look at the module hierarchy, maybe you language just requires too much boilerplate to stand on its own.
At the end of the day, ask yourself. How many poets actually used correct grammar in their writings. Formalized concepts are overrated and sometimes it's necessary to break the rules.