다음을 통해 공유


Design Guidelines Digest

Lots of people have asked me to create a short version of the Design Guidelines. Here it is. You can also email me directly at kcwalina@microsoft.com if you would like to get an MS Word copy of the digest, hich has a bit better formatting.

[UPDATE: I recently updated this document and placed it online. The details are described here.]

Also, I would be interested in knowing what you think about the selection of the guidelines in the digest. Have I ommited something important? Have I included something that could be cut?

API Design Guidelines Digest

Krzysztof Cwalina (kcwalina@microsoft.com)

Program Manager, Microsoft

Introduction

The full .NET Framework Design Guidelines document consists of more than 200 pages of detailed prescriptive guidance. In can be accesses at MSDN. This document is a distilled version highlighting the most important of those guidelines.

Moving to a managed execution environment (the .NET Framework) offers an opportunity to improve the programming model. For several reasons, we strongly advise designers to treat the Design Guidelines as if they were prescriptive. We believe that developer productivity can be seriously hampered by inconsistent design.

Development tools and add-ins will turn some of these guidelines into de facto prescriptive rules, and reduce the value of non-conforming components. These non-conforming components will function, but not to their full potential.

It is very important that you follow the guidelines provided here. However, there are instances where good library design dictates that these guidelines be broken. In such cases, it is important to provide solid justification.

General Design Principles

Scenario Driven Design: Start the design process of your public API by defining the top scenarios for each feature area. Write code you would like the end users to write when they will be implementing these scenarios using your API. Design your API based on the sample code you wrote.

Usability Studies: Test usability of your API. Choose developers who are not familiar with your API and have them implement the main scenarios. Try to identify which parts of your API are not intuitive.

Self Documenting API: Developers using your API should be able to implement main scenarios without reading the documentation. Help users to discover what types they need to use in main scenarios and what the semantics of the main methods are by choosing intuitive names for most used types and members. Talk about naming choices during specification reviews.

Understand Your Customer: Realize that the majority of your customers are not like you. You should design the API for your customer, not for developers working in your close working group, who unlike majority of your customers are experts in the technology you are trying to expose.

Casing & Naming Guidelines

Casing and naming guidelines apply only to public and protected identifiers, and privately implemented interface members. Teams are free to choose their own guidelines for internal and private identifiers.

Do use PascalCasing (capitalize the first letter of each word) for all identifiers except parameter names. For example, use TextColor rather than Textcolor or Text_color.

Do use camelCasing (capitalize first letters of each word except for the first word) for all parameter names.

Do use PascalCasing or camelCasing for any acronyms over two characters long. For example, use HtmlButton rather than HTMLButton, but System.IO instead of System.Io.

Do not use acronyms that are not generally accepted in the field.

Do use well-known acronyms only when absolutely necessary. For example, use UI for User Interface and Html for Hyper-Text Markup Language.

Do not use of shortenings or contractions as parts of identifier names. For example, use GetWindow rather than GetWin.

Do not use underscores, hyphens, or any other non-alphanumeric characters.

Do not use Hungarian notation.

Do name types and properties with nouns or noun phrases.

Do name methods and events with verbs or verb phrases. Always give events names that have a concept of before and after using the present particle and simple past tense. For example, an event that is raised before a Form closes should be named Closing. An event raised after a Form is closed should be named Closed.

Do not use the “Before” or “After” prefixes to indicate pre and post events.

Do use the following prefixes:

· “I” for interfaces.

· “T” for generic type parameters (except single letter parameters).

Do use the following postfixes:

· “Exception” for types inheriting from System.Exception.

· “Collection” for types implementing IEnumerable.

· “Dictionary” for types implementing IDictionary or IDictionary<K,V>.

· “EventArgs” for types inheriting from System.EventArgs.

· “EventHandler” for types inheriting from System.Delegate.

· “Attribute” for types inheriting from System.Attribute.

Do not use the postfixes listed above for any other types.

Do not postfix type names with “Flags” or “Enum”.

Do use plural noun phrases for flag enums (enums with values that support bitwise operations) and singular noun phrases for non-flag enums.

Do apply FlagsAttribute to flag enums.

Do use the following template for naming namespaces: <Company>.<Technology>[.<Feature>]. For example, Microsoft.Office.ClipGallery. Operating System components should use System namespaces instead for the <Company> namespaces.

Do not use organizational hierarchies as the basis for namespace hierarchies. Namespaces should correspond to scenarios regardless of what teams contribute APIs for those scenarios.

General Design Guidelines

Do use the most derived type for return values and the least derived type for input parameters. For example take IEnumerable as an input parameter but return Collection<string> as the return type.

Do provide a clear API entry point for every scenario. Every feature area should have preferably one, but sometimes more, types that are the starting points for exploring given technology. We call such types Aggregate Components. Implementation of large majority of scenarios in given technology area should start with one of the Aggregate Components.

Do write sample code for your top scenarios. The first type used in all these samples should be an Aggregate Component and the sample code should be straightforward. If the code gets longer than several lines, you need to redesign. Writing to an event log in Win32 API was around 100 lines of code. Writing to .NET Framework EventLog takes one line of code.

Do model higher level concepts (physical objects) rather than system level tasks with Aggregate Components. For example File, Directory, Drive are easier to understand than Stream, Formatter, Comparer.

Do not require users of your APIs to instantiate multiple objects in main scenarios. Simple tasks should be done with one new statement.

Do support so called “Create-Set-Call” programming style in all Aggregate Components. It should be possible to instantiate every component with the default constructor, set one or more properties, and call simple methods or respond to events.

EventLog applicationLog = new EventLog();

applicationLog.Source = “MySource”;

applicationLog.WriteEntry(exception.Message);

Do not require extensive initialization before Aggregate Components can be used. If some initialization is necessary, the exception resulting from not having the component initialized should clearly explain what needs to be done.

Do carefully choose names for your types, methods, and parameters. Think hard about the first name people will try typing in the code editor when they explore the feature area. Reserve and use this name for the Aggregate Component. A common mistake is to use the “best” name for a base type.

Do Run FxCop on your libraries.

Do ensure your library is CLS compliant. Apply CLSCompliantAttribute to your assembly.

Do prefer classes over interfaces.

Do not seal types unless you have a strong reason to do it.

Do not create mutable value types.

Do not ship interfaces without providing at least one default implementation (a type implementing the interface). This helps to validate the interface design.

Do not ship interfaces without providing at least one API consuming the interface (a method taking the interface as a parameter). This helps to validate the interface design.

Avoid public nested types.

Do strongly prefer collections over arrays in public API.

Do provide strongly typed collections.

Do not use ArrayList, List<T>, Hashtable, or Dictionary<K,V> in public APIs. Use Collection<T>, ReadOnlyCollection<T>, KeyedCollection<K,T>, or CollectionBase subtypes instead. Note that the generic collections are only supported in the Framework version 2.0 and above.

Do not use error codes to report failures. Use Exceptions instead.

Do not throw Exception or SystemException the base type.

Avoid catching the Exception base type.

Do prefer throwing existing common general purpose exceptions like ArgumentNullException, ArgumentOutOfRangeException, InvalidOperationException instead of defining custom exceptions.

Do throw the most specific exception possible.

Do ensure that exception messages are clear and actionable.

Do provide delegates with signatures following the pattern below far all events: <EventName>EventHandler(object sender, <EventArgs> e)

Do prefer event based APIs over delegate based APIs.

Do prefer constructors over factory methods.

Do not expose public fields. Use properties instead.

Do prefer properties for concepts with logical backing store but use methods in the following cases:

· The operation is a conversion (such as Object.ToString())

· The operation is expensive (orders of magnitude slower than a field set would be).

· Obtaining a property value using the Get accessor has an observable side effect.

· Calling the member twice in succession results in different results.

· The member returns an array. Note: Members returning arrays should return copies of an internal master array, not a reference to the internal array.

Do allow properties to be set in any order. Properties should be stateless with respect to other properties.

Do not make members virtual unless you have a strong reason to do it.

Avoid finalizers.

Do implement IDisposable on all types acquiring native resources and those that provide finalizers.

Do be consistent in the ordering and naming of method parameters.

It is common to have a set of overloaded methods with an increasing number of parameters to allow the developer to specify a desired level of information. The Make sure all the related overloads have a consistent parameter order (same parameter shows in the same place in the signature) and naming pattern.

public class Foo{

   readonly string defaultForA = "default value for a";

   readonly int defaultForB = 42;

   

   public void Bar(){

      Bar(defaultForA, defaultForB);

   }

          

   public void Bar(string a){

      Bar(a, defaultForB);

   }

           

   public void Bar(string a, int b){

      // core implementation here

   }

}

The only method in such a group that should be virtual is the one that has the most parameters and only when extensibility is needed.

Avoid out and ref parameters.

Resources

About FxCop

FxCop is a code analysis tool that checks .NET managed code assemblies for conformance to the Microsoft .NET Framework Design Guidelines. It uses reflection, MSIL parsing, and callgraph analysis to inspect assemblies for more than 200 defects in the following areas:

· Library design

· Localization

· Naming conventions

· Performance

· Security

FxCop includes both GUI and command line versions of the tool, as well as an SDK to create custom rules. The tool can be downloaded from https://www.gotdotnet.com/team/fxcop

Other Resources

Full Design Guidelines can be accessed at https://msdn.microsoft.com/library/en-us/cpgenref/html/cpconNETFrameworkDesignGuidelines.asp. These provide some more detail and some justifications for the guidelines described above.

Design Guideline updates are posted to the following blog. https://blogs.msdn.com/kcwalina/archive/2004/06/22.aspx

Comments

  • Anonymous
    September 28, 2004
    -- "Do not require users of your APIs to instantiate multiple objects in main scenarios. Simple tasks should be done with new statement." I could not understand the second sentence. Do you mean "done with additional statements"?

    -- "Do name types and properties with nouns or noun phrases." This is nice, but often I cannot think of a noun for a property, especially boolean properties. Perhaps there is another suggestion as well?

    -- The Aggregate design and scenario planning suggestions are great.

    -- "Do not seal types unless you have a strong reason to do it." This implies more testing, since unsealed types must be tested in derived class scenarios, as well as all the usual scenarios. You are putting a greater burden on the designer and implementer by asking them to leave things unsealed -- costs that may not be recognized. Perhaps this cost should be mentioned.

    -- "Do not create mutable value types." It is impossible to change one property on a value type without calling a constructor, if it is immutable. This can produce clumsy code, especially when you need to put the value type "back" into something (like an array). For example, the Font class, though not a value type, is immutable and extremely clumsy.

    -- "Avoid public nested types." I thought it was recommended to use an nested struct Enumerator type for enumerators? At one point I picked up this idea from MS.




  • Anonymous
    September 28, 2004
    The comment has been removed

  • Anonymous
    September 28, 2004
    A brief antithesis

    We have:
    "Do not use of shortenings or contractions as parts of identifier names. For example, use GetWindow rather than GetWin"

    But also:
    ""EventArgs" for types inheriting from System.EventArgs."

    Slight contradiction, maybe it should have been EventArguments :-)

  • Anonymous
    September 29, 2004
    "new statement" -- That phrase is ambiguous as it can be interpreted two ways. As a statement using the "new" operator, or an additional statement.

    I understand why Peter Golde was confused by mutable types, but mutable structs also have the potential to produce more efficient optimized code by the jitter. If not now, perhaps in the future. For example if you want to change all the x values of points in a huge array, with an immutable struct you must copy over all the y values as well. As far as I know it is only possible to modify an embedded struct in an array. But that is enough reason right there I think to justify mutable structs, since arrays can be giant, aside from the clumsiness argument.

    To get rid of the confusion caused by mutable structs in Peter Golde's case, I would recommend avoid creating structs that contain reference variables (references to objects). The compiler can catch property assignments to temporaries.

  • Anonymous
    September 29, 2004
    Ripster, you are right that there are many places in the guidelines and framework that can be seen as contradictions. History, schedule, “design by committee”, and finally simple mistakes cause them. I wish the guidelines and the framework were perfect (from my point of view of course :-)), but this is really not possible given the complexities. As the owner of the project I used to get frustrated with any of the “flaws”. Brad Abrams cured me saying “it’s way better than it used to be”. :-)

  • Anonymous
    September 29, 2004
    It might be helpful to have links from the each of the "distilled" guidelines to their corresponding full explanations in the full design guidelines site.

    On the frustration with "flaws"... If there were no flaws, we might not see the value in seeking to prevent them with guidelines. :)

  • Anonymous
    September 30, 2004
    Can you possibly post a PDF version of the word doc?

    Peter

  • Anonymous
    October 01, 2004
    "Do prefer classes over interfaces."

    I try and work Test first which says to favour interfaces over classes. This is because it affords the developer more options to "mock" / stub an object he's code must collaborate with if that object is behind an interface.

    Mocking a collaborating object is an important tool in the test first developer’s toolkit.

    Thanks
    Christian

  • Anonymous
    October 01, 2004
    The comment has been removed

  • Anonymous
    October 07, 2004
    Peter, please email me at kcwalina@microsoft.com and I will send you PDF version of the digest.

    Christin, I have a post on classes vs. interfaces on my "todo" list for blogging, and once I get to it, it will provide much more through reasoning for the guideline. But quickly to address you comment; a pure abstract class is no worse than an interface in terms of the flexibility that you describe.

  • Anonymous
    October 09, 2004
    "Do prefer constructors over factory methods"

    this one is rather weird and - if followed blindly - it can have a negative impact on extensibility and maintainability

    why would you enforce a constructor and ignore the obvious benefits of a factory (f.e. ability to plug a different implementation of an interface, ability to return cached instances instead of creating new ones, ability to return a single instance, etc, etc)?

  • Anonymous
    October 11, 2004
    Yes, if the guideline is followed blindly, it can have a negative impact. The same holds true for the large majority of other guidelines. With the exception of naming conventions and some small number of design guidelines (for which the only justification is consistency), guidelines are not to be followed blindly. They are just good rules of thumb (starting/default points). If you find yourself violating the guidelines from time to time and you have a good reason, it’s ok (if you violate them most of the time then either we have developed bad rules of thumb or you indeed are doing something that is not consistent with the guidance).
    Also on general note, I now see clearly the problem with the Digest. It’s short, but it forced me to delete all the justification and background that the original document has. Without the background info, it’s really difficult to judge whether not adhering to a guideline is the right thing or not. For example, here is the full text of the factory vs. constructor guidelines: http://blogs.msdn.com/kcwalina/archive/2004/10/11/241027.aspx

  • Anonymous
    October 14, 2004
    An update to the Factory vs Constructor design guidelines ...

  • Anonymous
    October 14, 2004
    An update to the Factory vs Constructor design guidelines ...

  • Anonymous
    June 02, 2006
    PingBack from http://info-bus.com/blog/2006/06/02/api-design-principles/

  • Anonymous
    August 30, 2006
    PingBack from http://www.morningtoast.com/index.php/2006/08/the-obvious-api/

  • Anonymous
    August 31, 2006
    An interesting general code guideline . Most things will sound familiar to the more experienced developer,

  • Anonymous
    September 14, 2006
    Forse ha ragione David: la mia risposta al post di Giulio (per quanto sintetica e quindi non esaustiva) non

  • Anonymous
    January 23, 2007
    Discussioni come questa sono un classico, e classica

  • Anonymous
    February 04, 2008
    PingBack from http://dstelow.wordpress.com/2008/02/04/links-for-2008-02-04/

  • Anonymous
    April 09, 2008
    Almost 4 years ago, I blogged about Framework Design Guidelines Digest . At that time, my blog engine

  • Anonymous
    May 25, 2008
    Regarding "Do not expose public fields. Use properties instead" I always saw this done in practice but was never sure why. I suppose you can't tell from the class instance when the field is accessed? Or?

  • Anonymous
    November 19, 2008
    The link to Full Design Guidelines does not work. Can you fix it please?

  • Anonymous
    December 09, 2008
    Thanks Filip! I fixed the link

  • Anonymous
    January 21, 2009
    PingBack from http://www.hilpers.it/2655011-design-guidelines

  • Anonymous
    June 16, 2009
    PingBack from http://fixmycrediteasily.info/story.php?id=10161

  • Anonymous
    June 18, 2009
    PingBack from http://outdoordecoration.info/story.php?id=1823