On Designing Good Libraries -- Part III (轉)

worldblog發表於2008-01-31
On Designing Good Libraries -- Part III (轉)[@more@]

Brad Abrams

 

Good discussion on the first two.... Let's see how this goes.

Fields:namespace prefix = o ns = "urn:schemas--com::office" />

?FONT face="Times New Roman" size=1> Never use publicly exposed instance fields

?FONT face="Times New Roman" size=1> Properties offer more flexibility at minimal cost

?FONT face="Times New Roman" size=1> JIT inlines simple property access

?FONT face="Times New Roman" size=1> Easy to add cache or delay creation in the future

?FONT face="Times New Roman" size=1> For static fields, do not include a prefix on a public field name

?FONT face="Times New Roman" size=1> Example: 'g_' or 's_' to distinguish static vs. non-static fields

const

?FONT face="Times New Roman" size=1> Compile-time evaluation

?FONT face="Times New Roman" size=1> Stable across versions

?FONT face="Times New Roman" size=1> Always static

readonly

?FONT face="Times New Roman" size=1> Run-time evaluation

?FONT face="Times New Roman" size=1> Unstable across versions

?FONT face="Times New Roman" size=1> Static or instance

class Math {

public const double Pi = 3.14;

}

class Color {

public static readonly Color Red = new Color(...);

public static readonly Color Blue = new Color(...);

public static readonly Color Green = new Color(...);

}

Properties

?FONT face="Times New Roman" size=1> Smart fields

?FONT face="Times New Roman" size=1> Calling syntax like fields

?FONT face="Times New Roman" size=1> Flexibility of methods

?FONT face="Times New Roman" size=1> Use read only properties where appropriate

?FONT face="Times New Roman" size=1> Do not use write-only properties

?FONT face="Times New Roman" size=1> Consr raising PropertyChanged events

?FONT face="Times New Roman" size=1> Property getters should be simple and therefore unlikely to throw exceptions

?FONT face="Times New Roman" size=1> Properties should not have dependencies on each other

?FONT face="Times New Roman" size=1> Setting one property should not affect other properties

?FONT face="Times New Roman" size=1> Properties should be settable in any order

?FONT face="Times New Roman" size=1> Common to have read-only public access and protected write access

public class MyClass {
private string name;
public string Name {
get {
return name;
}
}
protected void SetName (string name)
{
this.name = name;
}
}

Properties versus Methods

?FONT face="Times New Roman" size=1> Do use a property:

?FONT face="Times New Roman" size=1> If the member has a logical data member

string Name {get;} //Good

string GetName() //Bad

Guid GetNext(){} //Good

Guid Next {get;} //Bad

?FONT face="Times New Roman" size=1> Do use a method:

?FONT face="Times New Roman" size=1> If the operation is a conversion, such as ToString()

?FONT face="Times New Roman" size=1> If the getter has an observable side effect

?FONT face="Times New Roman" size=1> If order of execution is important

?FONT face="Times New Roman" size=1> If the method might not return immediately

?FONT face="Times New Roman" size=1> If the member returns an array

EmployeeList l = FillList();

for (int i = 0; i < l.Length; i++) {

if (l.All[i] == x){...}

}

//calling code:

if (l.GetAll()[i]== x) {...}

?FONT face="Times New Roman" size=1> Creates a new snap shot of the array each time through the l

?FONT face="Times New Roman" size=1> The GetAll() fomakes this much clearer

Properties: Indexers

?FONT face="Times New Roman" size=1> Use if the logical backing store is an array

?FONT face="Times New Roman" size=1> Almost always int or string indexed

?FONT face="Times New Roman" size=1> Rare to have multiple indices

public char this[int index] {get;}

String s = "foo";

Console.WriteLine (s[i]); // calls indexer

Events

?FONT face="Times New Roman" size=1> Defining an event

public delegate void EventHandler( sender,

EventArgs e);

public class Button: Control {
public event EventHandler Click;

protected void OnClick(EventArgs e) {
if (Click != null)

Click(this, e);
}

}

?FONT face="Times New Roman" size=1> Using an Event

void Initialize() {

Button b = new Button(...);

b.Click += new EventHandler(ButtonClick);

}

void ButtonClick(object sender, EventArgs e)

{

MessageBox.Show("You pressed the button");

}

?FONT face="Times New Roman" size=1> Events are raised, not triggered or fired

?FONT face="Times New Roman" size=1> Name events with a verb

?FONT face="Times New Roman" size=1> E.g.: Click, Paint, DrawItem, DropDown,

?FONT face="Times New Roman" size=1> Event handlers have void return type

public delegate void MouseEventHandler (
object sender,
MouseEventArgs e);

?FONT face="Times New Roman" size=1> Event handler delegates use a signature that follows the event design pattern

?FONT face="Times New Roman" size=1> Use strongly typed event data where appropriate

public class MouseEventArgs :
EventArgs { }

?FONT face="Times New Roman" size=1> Able to add new members without a breaking change

?FONT face="Times New Roman" size=1> Provide a protected method to raise the event

?FONT face="Times New Roman" size=1> Named OnEventName

?FONT face="Times New Roman" size=1> Make it virtual if extensibility is needed

public class Button {
private ButtonClickHandler onClickHandler;
protected void OnClick (ClickEventArgs e) {
if (onClickHandler != null) {
// call the delegate if non-null
onClickHandler(this, e);
}
}
}

?FONT face="Times New Roman" size=1> Events are callbacks into arbitrary user code

?FONT face="Times New Roman" size=1> Do not assume anything about the state of your object

?FONT face="Times New Roman" size=1> Code defensively

protected void DoClick() {
PaintDown(); // paint button in depressed state
try {
OnClick(); // call event handler
}
finally {
// window may be deleted in event handler
if (windowHandle != null) {
PaintUp(); // paint button in normal state
}
}
}

Static Members

?FONT face="Times New Roman" size=1> Any kind of member can be static

?FONT face="Times New Roman" size=1> Static members

?FONT face="Times New Roman" size=1> Cannot access instance state

?FONT face="Times New Roman" size=1> Cannot override or specialize

?FONT face="Times New Roman" size=1> Should be thread-safe

?FONT face="Times New Roman" size=1> Commonly used for

?FONT face="Times New Roman" size=1> Singleton pattern

?FONT face="Times New Roman" size=1> Utility methods

?FONT face="Times New Roman" size=1> Statics are the ..NET equivalent of global variables or global functions

?FONT face="Times New Roman" size=1> Not object oriented

?FONT face="Times New Roman" size=1> Same evils as global

?FONT face="Times New Roman" size=1> But can be very useful, e.g., System.Math

Singleton Pattern

?FONT face="Times New Roman" size=1> Ensures that a class has only one instance and provide a global point of access to it

?FONT face="Times New Roman" size=1> This is not exactly the GoF pattern (see threading section)

public sealed class Null {
private DBNull() {}
public static readonly DBNull Value = new DBNull();
// instance Methods...

}

//Calling code

if (x == DBNull.Value) {..}

?FONT face="Times New Roman" size=1> Notice this class:

?FONT face="Times New Roman" size=1> Is sealed to prevent sub-classing to add instances

?FONT face="Times New Roman" size=1> The Value is static for easy access

?FONT face="Times New Roman" size=1> The Value is readonly so it cannot be modified

?FONT face="Times New Roman" size=1> Has a private constructor

?FONT face="Times New Roman" size=1> The instance is immutable

?FONT face="Times New Roman" size=1> No methods that can mutate its state

Parameter Passing

?FONT face="Times New Roman" size=1> Value types and Reference types can both be passed by value or by reference

?FONT face="Times New Roman" size=1> A value type by value copies the value

?FONT face="Times New Roman" size=1> No side effects

?FONT face="Times New Roman" size=1> Commonly used
public int Add (int x, int y) {..}

?FONT face="Times New Roman" size=1> A value type by reference uses a pointer to the value

?FONT face="Times New Roman" size=1> Side effects possible

?FONT face="Times New Roman" size=1> Rarely used
public static int (ref int location, int value) {..}

?FONT face="Times New Roman" size=1> A reference type by value copied the reference

?FONT face="Times New Roman" size=1> Side effects are possible on mutable types

?FONT face="Times New Roman" size=1> Commonly used
public void Insert (object value) {..}

?FONT face="Times New Roman" size=1> A reference type by reference uses a pointer to the reference variable

?FONT face="Times New Roman" size=1> Side effects possible

?FONT face="Times New Roman" size=1> Almost never used
public static int Method (ref object moreData) {..}

?FONT face="Times New Roman" size=1> Using Ref and Out parameters

?FONT face="Times New Roman" size=1> Primarily used for interop

?FONT face="Times New Roman" size=1> Avoid directly exposing publicly

?FONT face="Times New Roman" size=1> May be used for extremely performance-sensitive areas

?FONT face="Times New Roman" size=1> Almost exclusively used with value types

?FONT face="Times New Roman" size=1> Ref is a CLR feature

?FONT face="Times New Roman" size=1> Out is a feature

?FONT face="Times New Roman" size=1> Out parameter semantics downgrade to Ref semantics in other languages

?FONT face="Times New Roman" size=1> Ref and out designs are less usable

public void GetLocation (
ref int x, out int y) {..}

//calling code

int x = 42;
int y;
b.GetLocation (ref x, out y);

public struct Point {
public int X {get;}
public int Y {get;}
}

//calling code

Point p = b.Location;

Argument Validation

?FONT face="Times New Roman" size=1> Do argument checking on every publicly exposed member

?FONT face="Times New Roman" size=1> Catches errors early (fail-fast)

?FONT face="Times New Roman" size=1> Much easier to de

?FONT face="Times New Roman" size=1> Powerful security precaution

?FONT face="Times New Roman" size=1> Throw meaningful exceptions

?FONT face="Times New Roman" size=1> Subclasses of ArgumentException

public int Count {
get {return count;}
set {
if (value < 0 || value >= MaxValue)
throw new ArgumentException(..);
}
}
public void (int start, int end) {
if (start < 0)
throw new ArgumentException(..);
if (end < start)
throw new ArgumentException(..);
}

 


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-998938/,如需轉載,請註明出處,否則將追究法律責任。

相關文章