views:

167

answers:

5

We are planning to build a framework: a cost-estimation framework that will be used across the domains in our organization.

The high-level requirement is something like this : If I develop a certain product, how much will it costs me? This generated cost will be used to compare with the cost the vendors have quoted and to come-up with a decision as to which vendor to choose.

Now, my question is: What things to considered while developing a framework?

Few of my thoughts:

  1. Implement the high-level requirements through Abstract classes and Interfaces
  2. Provide utility classes that might be useful for Framework users.
  3. Consider what should be internal - kind of metadata - that shouldn't be shown to framework users.
  4. The design patters to use like template.
  5. the properties and the methods of the input classes.
+7  A: 

A few ideas:

  • It's easier to add useful features later than to remove features which have proved to be badly designed or harmful.
  • Design for inheritance or prohibit it: inheritance introduces a whole extra layer of complexity, as you need to work out the interactions between superclasses and subclasses. That's not to say it's evil, but it should be very carefully considered.
  • Interfaces are usually cleaner than abstract classes in my experience, as they promote composition over inheritance.
  • For interfaces, document both what the caller should expect and what the implementer should expect. Basically think of the contract from both sides, and document it. In particular, document nullity constraints - should methods accept null or not? Should they guarantee that they'll never return null?
  • Design for testability, both of your framework and others using your framework. Which bits of the framework can reasonably be used in test code, and which should be mocked out?
  • Use your own framework, right from the start. Build a sample application which others can use to understand the framework.
Jon Skeet
I'd also add prefer dependency injection to direct instantiation/service look-up (although the designing for testability normally mandates this).
Nick Holt
+1 for keep it as simple as possible.
Thorbjørn Ravn Andersen
Just to give a counter argument. For rapidly evolving frameworks (and therefore most new ones) use of interfaces is often discourgaged. See "Interfaces are overvalued" - http://www.artima.com/weblogs/viewpost.jsp?thread=142428
Pablojim
+2  A: 

Here's how I'd proceed:

  1. do a broad high-level design - things like is it a web-app or thick client, is there a middle tier, how will the DB interaction happen, what APIs/technologies will be used, etc.
  2. pick a feature that will allow you do code though all tiers of the application (what's commonly referred to as a spike).
  3. implement the bare minimum to make that feature work (Keep It Simple, Stupid) together with tests to prove it works.
  4. pick another feature goto 3 (refactoring as necessary).

I've always found this way of working evolves the system and that actually making something work focuses your design on what is essential (rather than the flights of fantasy that can happen with paper designs).

And there was me thinking the agilist in me was gone ;-)

Nick Holt
+2  A: 

Aside from the general programming advice, it's worth looking into some of the basics of the theory of software framework design. For a start look into the concept of "hot spots" and "frozen spots". While this mightn't appear immediately useful it is good to have in the back of your mind while developing.

As always wikipedia is a good starting point:

http://en.wikipedia.org/wiki/Software_framework

Also a good summary here:

http://www.acm.org/crossroads/xrds7-4/frameworks.html

If you want to go deeper have a look at the citations in both these articles.

Pablojim
A: 

Use interfaces in API contracts. This allows you to keep the messy details completely decoupled and easily decorate them if needed. (Just see Properties which are thinly disguised Maps).

A very good tip is to use Test Driven Design - i.e. write the test FIRST and then the implementation. This forces you to think like the USERS instead of the designer, which eventually will lead to a better API.

Thorbjørn Ravn Andersen
+1  A: 

Some of the advice below depends on whether you are building a framework strictly for internal use by a small team on a limited set of projects, or whether you are building something for use by many anonymous developers. Also, keep in mind that much depends on the size of your framework and how broad or narrow it's scope is. Some of my advice really only applies to larger, multi-purpose frameworks.

General Advice

  • Take the time to talk to some of the developers who will be using your framework. Make sure you actually understand what will be useful and what will not.
  • Take the time to make sure the abstraction you are creating is comprehensible and intuitive to your future users.
  • Unless you are building something unique and revolutionary, take the time to look at other frameworks that exist in your domain. Avoid their mistakes but don't be afraid to borrow successful concepts ... not everything has to be invented by you.
  • Produce documentation - both in the form of formal API docs as well as sample code. If people can't understand how to apply your framework it won't succeed.
  • Name things clearly but concisely. This is probably one of the hardest tasks for framework developers - especially when implementing abstract or generalized concepts. Talk to people before settling on a name .. some names have multiple meanings and may be interpreted differently than you might expect.
  • Consider making your framework compatible with other low level frameworks (like those for logging, mocking, and dependency injection). This will make your users happy and reduce the barriers to adoption.
  • Invest in good unit tests. Frameworks generally have a large surface area that users can interact with ... many classes, many methods, derivation, interface implementations. Without an ability to rapidly regression test, frameworks can rapidly grow beyond a level where they can be maintained.
  • Keep your public interfaces narrow and small. Keep the single responsibility principle in mind when designing functionality in a framework... it really matters at this level.

Design Advice

  • Design and test for parallelism. Most frameworks these days are used to build application with some (and often a lot) of parallel processing activity. Frameworks intended to be used in web applications, especially, need careful thought around how multithreaded access will affect them. Some general principles I follow are:
    • Avoid locks when possible. When not possible, use explicit locking objects rather than .NET lock( ... ) statements.
    • Use Reader/Writer locks for shared objects that are read more frequently than written.
  • Design functionality into in layers. For large frameworks, this helps to not only keep functionality decoupled but also allows users to more readily use just the portion of your framework they actually need.
  • For frameworks intended for use with .NET 2.0 and up, use generic collections! It is extremely hard to reason about what is stored in an object[], ArrayList or Hashtable ... especially in a large framework where such collections are passed around internally.
  • Avoid using object or object[] in the public interface ... this is evil in a framework and is a leading cause of bugs and maintainability problems. You can almost always find an interface or use generics in place of this. In those rare cases you can't, document exactly what you expect to be passed in or out ... and assert for it.
  • Prefer aggregation to inheritance. While inheritance is a useful and powerful concept, framework authors often over use or misuse it. Think carefully about whether inheritance is truly the right tools to solve a design problem.
  • Avoid type casts and runtime type checks when possible. These are generally a design-smell in my experience. Lot's of casting indicates you are not taking advantage of interfaces, generics, or generic constraints effectively. This can lead to bugs and confusion on the part of developers as to how to use your framework.
  • Allow callers to inject functionality using strategies when possible. Inheritance is overkill for certain kinds of specialization. With the level of support functions get as citizens in the .NET ecology, you should look to use them (in the form of lambdas or delegates) to allow consumers to specialize the functionality you provide.

Implementation Advice

  • Avoid properties whose getters produce side-effects. Sometimes this is unavoidable (such as getters that perform internal caching, or lazily instantiate objects). However, in my experience, getters with side-effects (particularly in framework level code) is the fastest way to introduce heisenbugs and make your users curse your name when debugging.
  • When possible, make small, transient objects immutable. Enforce this using the readonly (or equivalent) keyword in the language. The benefits to immutability are tremendous - and in .NET (where allocation costs are reduced) it's not as expensive as you might think.
  • Use self-encapsulation when possible. This technique helps avoid bugs and simplifies refactoring by avoiding different access semantics to data for internal vs. external callers.
  • Avoid magic numbers and hard-coded constants. A framework is one type of code where it is very hard to anticipate your users exact needs. Leave some flexibility by using configuration rather than compiled-in constants.
  • Keep the number of parameters to methods small (less than 7). When you need to pass in many parameters, consider creating a lightweight data object to support the method.
  • Prefer generic methods to multiple method overloads for different types. Let the language and compiler do the work when possible. This also allows your code to be more flexible and useful. Examine the design of LINQ for how this works in practice.
LBushkin