Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

In my current development situation, we have a lot of DLLs, executables, and static libraries. How do you decide what should go into a DLL? What should go into an executable? Why have separate functionality in different executable files? I'm hoping the answer will be concise, but this is a largely opinionated topic it would seem.

How do you decide where functionality resides in a large-scale project (more than one executable file)? I'm expecting answers to range from, "Good Design" to "Modularity" to "Whatever management puts in a requirements doc".

share|improve this question
add comment

3 Answers

up vote 11 down vote accepted

Choosing between a library and an executable file is relatively simple: does it make sense to execute the code you want to put into an executable as a standalone program? If not, it should probably be a library. In general, I would favour a thin executable layer over as many libraries as needed, since that makes it easier to reuse those backend libraries later and they are not tied to a particular program.

Far as deciding on how to split your code between libraries goes, you might find Uncle Bob Martin's article on granularity useful.

In it he talks about OO structure and defines several principles that can help you package your code appropriately. These are also covered in more detail in his book, Agile Principles, Patterns, and Practices in C#.

I will summarize the principles below:

The Reuse/Release Equivalence Principle (REP)

The granule of reuse is the granule of release. Only components that are released through a tracking system can be effectively reused. This granule is the package.

Uncle Bob defines reuse as being able to statically or dynamically link the reused library into his program and never having to look at its source code. When a new version of the library is released, he can just integrate it into his system.

Treating libraries this way drives only keeping related things together in the same package. Otherwise, the library's consumers might have to upgrade to a new version for no reason or lag a few versions behind.

The Common Reuse Principle (CRP)

The classes in a package are reused together. If you reuse one of the classes in a package, you reuse them all.

This principle supports the one above. If you have classes in the same package that aren't related to one another, you may be forcing the users of your library to upgrade unnecessarily.

The Common Closure Principle (CCP)

The classes in a package should be closed against the same kinds of changes. A change that affects a package affects all the classes in that package.

This principle talks about maintainability. The idea here is to group classes based on how they might need to change. That way your changes can be localized to one part of the application and not spread all over.

The Acyclic Dependencies Principle (ACP)

The dependency structure between packages must be a Directed Acyclic Graph (DAG). That is, there must be no cycles in the dependency structure.

Disallowing cyclic dependencies allows each package to be developed independently and "released" to the rest of the company when new changes are ready. This way you don't end up with two teams deadlocked, waiting on each other to finish some work.

share|improve this answer
add comment

What's going to make it easier to maintain the code in the long run? If you have unrelated functions in the same component (i.e., dll, exe, compiled unit), and you have to change one function, is that going to break the other? When you make a change on a component, you'll need to retest all the downstream components depending on ALL the functions in that component. If you limit the functionality within each component, changing each one will be less risky.

Also, By focusing components on a small number of closely related functionality, you'll have a much easier time proving that that one component behaves the way it should.

share|improve this answer
    
Always think of the "DLL Hell" scenario. That is as Mr. Flynn said, think what happens when something changes. Always know your dependencies very well and make sure you can cover the install scenarios without pain to the end-user. –  Emmad Kareem Aug 29 '11 at 20:15
add comment

A simple way to approach an answer is to recognize that "DLL" stands for "dynamic-link library," and the key term is "library."

What do you want in a normal library? Good books that will be shared by many readers (users); useful items that gather dust until referenced by a reader (user) to answer a critical question at a perhaps desperate moment; and resources that link readers (users) to other resources. Code libraries are similar. We keep such things as frequently shared code, specialized or high-value reference modules, and architectural framework resources in them. Software libraries can be represented in several kinds of code artifacts, such as scripts, static libraries, dynamic libraries, components, and resource files.

Generally, I recommend making your executable modules act somewhat like scripts. They clearly outline and manage the main structure and flow of your system but call upon resources from your libraries to handle the nitty-gritty details. I find this approach better than muddying up high-level logic with confusing and overly specialized and technical low-level implementation concerns.

When considering how to allocate your code between executables and libraries, you need to consider both logical design and physical design.

In object-oriented systems, for example, it is important to logically organize your code to correctly assign responsibilities to the right methods in the right classes. This is part of logical solution design. Your logical design should be clear, clean, and lean and should be expressed in terminology that relates well to your users' domain.

When you plan to actually install your system on a user's site, you may be concerned to create a physical design that specifies how you will bundle your code into a set of easily deployable software resource objects (usually files) that can be readily mixed and matched to the needs of a particular target system. Determining which resources belong in which deployment package may generally involves some specific physical design considerations that have nothing directly to do with logical design. For example, you may want to swap out certain image file processing libraries depending on which customer will receive a given set of deployment objects.

In practice, you will find that good logical design usually leads to good physical design.

To summarize, the most important principle is intelligent packaging. By organizing your code materials into strongly related libraries, you end up with useful deployable code artifacts that you can easily re-use, move around, or give away.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.