Wednesday, September 14, 2005

Framework Design Guidelines: The Art of Building a Reusable Class Library

This was a fabulous session. Krzysztof Cwalina and Brad Abrams did an amazing job. Not only was the content incredibly good, but they did a fabulous job presenting it. They provided review questions, role playing, and even a lab! I will be picking up a copy of their new book Framework Design Guidelines : Conventions, Idioms, and Patterns for Reusable .NET Libraries. They spell out all the best practices for framework design.

  • If the size of the data is less then 16 bytes use a struct instead of a class.
  • You can just seal individual virtual methods instead of the whole class.
  • In .NET 2.0 you can make static types - will prevent common mistakes like having a public constructor (which comes from System.Object automatically.)
  • Use try-parse pattern when possibility of an exception in a tight loop ( page 204 of the book ).
When you are building a framework, you are building it for someone else, not you. Focus on the developer, not you. They cannot read your mind. Need to communicate to the user, documentation is good, but not the entire solution. API should be obvious. Good documentation on a bad API is like lipstick on a pig.

Communicate with consumers of an API by leaving artifacts in Types, Methods, Properties, Events, Constructors.

Communication Vocabulary

  • Namespaces
    • Organizational principle to allow consumers to:
      • Find relevant functionality quickly
      • Exclude less relevant functionality
    • Not about implementation issues
      • Security, identity, size, perf
      • A namespace can span multiple assemblies, and an assembly can span multiple namespaces.
    • "Not an advertising billboard"
      • Don't use organizational names in the namespace since it could change
  • Class
    • A conceptual model for a thing which can hold state, perform actions, etc.
    • Common API design problems
      • Grab bag types (lack of cohesion)
        • One class that does a lot of unrelated things.
    • Modeling overly abstract concepts
      • A File is simple, StreamReader is complex.
  • Struct
    • A domain specific extension of the intrinsic type system
      • Example: defining point, complex, etc.
    • Expert use: perf optimization when GC-Heap allocated objects not warranted
      • Example: an enumerator.
    • Common API design problems:
      • Overuse to avoid GC
      • Instance size over 16 btres
      • Are not immutable
  • Static Class
  • Exceptions
    • Only define exceptions that will be caught and handled differently then others.
    • Define exceptions to contain useful data
  • Enum
    • Specify values for enum's incase there is a binary change
    • Don't use "magic" constants
    • (all enums are stucts with one field - usually an int)
  • Flag Enums
    • Plural types
    • Combinable enum
    • FlagsAttribute
    • Powers of 2 instead of sequential numbering - bit masking
    • Provide common combinations
  • Constructors
    • Facility to capture the state of an instance
    • Do minimal work - don't do processes, may not be necessary - only store the state
  • Methods vs Properties
    • Expose actions or operations
    • Use properties
      • If the member is a logical backing store - it returns internal data
    • Use methods
      • If it is a conversion of types
      • If the getter could have an observable side effect
      • If order of execution is important
      • If the method might not return immediately
      • If the member returns an array
      • If the operation is expensive
  • Fields
    • Don't use - locks you into an implementation
  • Events
    • Events are "raised" not "fired" or "triggered" - that seems like an odd choice of terminology to me.
    • Use verbs - Click, Paint, etc.
    • Use strongly typed EventArgs
      • Using EventArgs allows for extensibility when new args are added it will remain backwards compatible.
  • Static Members
    • Global access
    • Not object orientated

Split some related functionality to two namespaces when part of it is only in 10% of the use cases.

  • Exceptions
    • Reuse an existing exception when possible
    • Define a new exception only when it will be handled different when caught.
    • Use separate exceptions for different errors (if will be handled differently)

Tags: [] [] [] []

No comments: