830 likes | 961 Views
10 Years of Framework Design Guidelines. Brad Abrams Product Unit Manager Microsoft Corporation http://blogs.msdn.com/brada Twitter: @ brada. Happy 10 Year Birthday. Happy 10 Year Birthday. Joyeux Anniversaire. Happy 10 Year Birthday. Proficiat met je verjaardag. Member Design.
E N D
10 Years of Framework Design Guidelines Brad Abrams Product Unit Manager Microsoft Corporationhttp://blogs.msdn.com/brada Twitter: @brada
Happy 10 Year Birthday JoyeuxAnniversaire
Happy 10 Year Birthday Proficiat met je verjaardag
Communicate via leaving artifacts • Framework Design Artifacts: • Properties • Methods • Events • Constructors
lazy public XmlFile(string filename) {this.filename = filename; } Constructors are public class XmlFile { string filename; Stream data; public XmlFile(string filename) { this.data = DownloadData(filename); } } • Do minimal work in the constructor • Be Lazy! • Only capture the parameters
Properties public class ArrayList { public int Count {get;}} • Property getters should be simple and therefore unlikely to throw exceptions • Properties should not have dependencies on each other • Setting one property should not affect other properties • Properties should be settable in any order
Properties versus Methods • Use a Property: • If the member logical attribute of the type • Use a method: • If the operation is a conversion,such as ToString() • If the getter has an observableside effect • If order of execution is important • If the method might not return immediately • If the member returns an array
Properties and returning arrays public Employee[] All {get{}} public Employee[] GetAll() {} Calling Code EmployeeList l = FillList(); for (int i = 0; i < l.Length; i++){ if (l.All[i] == x){...} } if (l.GetAll()[i]== x) {...} Moral: Use method if the operation is expensive
Extension Methods namespace MyCompany.StringManipulation { public static class StringExtensions{ public static bool IsNullOrEmpty(this string s){ return String.IsNullOrEmpty(s); } } } … using MyCompany.StringManipulation; string message= “hello world”; if(message.IsNullOrEmpty()){ Console.WriteLine(“EMPTY”); }
Extension methods marry the usability offered by object-oriented APIs with the flexibility of functional APIs.
CONSIDER using extension methods to "add" methods to interfaces public interface IFoo{ void Bar(string x, bool y); void Bar(string x); } public static class IFooExtensions{ public static void Bar(this IFoo foo, string x){ foo.Bar(x,false); } } … IFoo foo = …; foo.Bar(“Hi!”);
CONSIDER using extension methods to manage dependencies Uri uri = “ftp://some.ftp.uri”.ToUri(); // higher level assembly (not mscorlib) namespace System.Net { public static class StringExtensions{ public static Uri ToUri(this string s){ … } } }
AVOID frivolously defining extension methods, especially on types you don’t own • Might add clutter • Choose namespaces for sponsor types carefully • Remember that not all languages support extension methods • Users will have to use static method call syntax
AVOID defining extension methods on System.Object // C# declaration of the extension method public static class SomeExtensions{ static void SomeMethod(this object o){…} } ‘ VB will try to find the method at runtime ‘ … but extension methods are resolved at ‘ compile time. Dim o As Object = … o.SomeMethod() ‘ THIS WILL THROW ‘ VB users will have to call the method using the regular static method call syntax. SomeExtensions.SomeMethod(o)
Framework Design Theater • The Main Character: • Bright young developer • The Setting: • Her first big project • The Setup: • Create a class that models a car • Actions required: Start and Drive
Design Pass One: Meets Requirements • Pass one: meets requirements
Time to Ship… Time to cut…
V.Next: Worse Yet • Now we want to add Color and Model, and we know exactly how • But it is much harder because the design is half done and mostly wrong
The moral Do as little as possible now (but no less) to ensure room for extensibility in the future
Abstract and Base classes • Prefer broad, shallow hierarchies • Less than or equal to 2 additional levels – Rough rule! • Contracts and responsibilities are difficult to maintain and explain in deep complex hierarchies • Consider making base classes not constructible (that is, use abstract classes) • Make it clear what the class is for • Provide a protected constructor for subclasses to call • System.Exception should not have had a public constructor
Virtual Method Example public class TheBase : Object { public override string ToString() { return “Hello from the Base"; } } public class Derived : TheBase { public override string ToString() { return “Hello from Derived"; } }
Virtual Methods • What is printed out? Derived d = new Derived();Console.WriteLine (d.ToString()); TheBase tb = d;Console.WriteLine (tb.ToString()); Object o = tb;Console.WriteLine (o.ToString());
Virtual Methods • They all output “Hello from Derived”. Why? • Method call virtualizes at runtime • The static type doesn’t matter • This is the danger and power of virtual methods • Danger: Owner of base classes cannot control what subclasses do • Power: Base class does not have to change as new subclasses are created
Overriding: Follow the Contract • Don’t change the semantics of member • Follow the contract defined on the base class All Virtual members should define a contract Don’t require clients to have knowledge of your overriding Should you call the base?
Virtual and non-virtual • Use non-virtual members unless you have specifically designed for specialization • Have a concrete scenario in mind • Write the code! • Follow the Liskov Substitution Principle • References to base types must work with derived types without knowing the difference • Must continue to call in the sameorder and frequency • Cannot increase or decrease range of inputs or output Barbara Liskov
Interface Usage public interface IComparable { int CompareTo(object obj);} • No common implementation (the ActiveX problem) • Challenging to version over releases • The smaller, more focused the interface the better • 1-2 members are best • But interfaces can be defined in terms of other simpler interfaces
The great proof of madness is the disproportion of one's designs to one's means.Napoleon Bonaparte
Type Dependency Management Careful dependency management is the necessary ingredient to successful evolution of frameworks. Without it, frameworks quickly deteriorate and are forced out of relevance prematurely.
DO NOThave upward dependencies Avoidhorizontal dependencies WPF XML BCL Reflection
CONSIDER placing library types higher on the dependency stack • Definition: • Library types are types that are not passed between components • Examples • EventLog, Debug, • Easy to Evolve • Leave old in, add new one • Beware of duplication!
DOkeep primitives policy free (i.e. simple) • Definition: • Primitive types are types that are passed between components and have very restricted extensibility (i.e. no subtype can override any members) • Examples • Int32, String, Uri. • Hard to Evolve • Little need to Evolve • Typically in lower layers
DO NOTcreate abstractions unless you know what you are doing • Definition: • Abstractions are interfaces or classes with unsealed members that are passed between components. • Examples • Stream, IComponent • Hard to Evolve • Unfortunately, pressure to evolve
The Pit of Success: in stark contrast to a summit, a peak, or a journey across a desert to find victory through many trials and surprises, we want our customers to simply fall into winning practices by using our platform and frameworks. To the extent that we make it easy to get into trouble we fail.- Rico Mariani
Is using your framework correctly like… Climbing a mountain?