Learning notes of Effective C + + (Clause 18: make the interface easy to be used correctly and not easy to be misused)

Keywords: C++

Recently, I started to watch Effective C + +. In order to facilitate future review, I specially took notes. If I misunderstand the knowledge points in the book, please correct!!!

Three rules must be observed when designing a good interface:

  • We must consider what kind of errors the user may make
  • The interface should be designed consistently
  • Don't ask users to remember that they have to do something

You must consider what errors users may make:

To develop an interface that is "easy to use correctly and not easy to be misused", you must first consider what errors users may make. Suppose you design a constructor for a class used to represent dates:

class Date{
public:
    Date(int month, int day, int year);  //American Standard, 2021-11-8, expressed as 11 / 8 / 21 in the United States
    ...
};

This interface is very reasonable in the United States, but its users are easy to make at least two mistakes:

  • Pass parameters in the wrong order

    Date d(8,11,2021);//British standard, 2021-11-8, British expressed as 8 / 11 / 21
    
  • An invalid month or day was passed

    Date d(2,30,2021);//It should be March 30, not February 30
    

We can customize the type to represent month, year and day, such as

class Day{
  	explicit Day(int d):val(d){}
    int val;
};

class Month{
  	explicit Month(int m):val(y){}
    int val;
};

class Year{
  	explicit Year(int y):val(y){}
    int val;
};

class Date{
    Date(const Month& m,const Day& d,const Year& y);
    ...
};

//Use Date type to represent 2021-11-8
Date d(8,11,2021);						//Error, parameter type mismatch
Date d(Day(8),Month(11),Year(2021));	//Error, parameter type mismatch
Date d(Month(11),Day(8),Year(2021));	//correct

After the problem of parameter type order is solved, sometimes the values of the parameters passed in are unreasonable. For example, the month is only from January to December. One way is to use enum to represent the month, but it does not have the type security we want. A safer way is to define all months in advance:

class Month{
public:
    static Month Jan(){return Month(1);}  
    static Month Feb(){return Month(2);}
    ...
    static Month Nov(){return Month(11);}
    static Month Dec(){return Month(12);} 
    ...
private:
    explicit Month(int m);  //explicit prohibits parameter implicit conversion, and private prevents users from generating custom months
    ...
};
//use
Date d(Month::Nov(), Day(30), Year(1995)); //correct

To design the interface consistently:

”Consistency "can make the interface easier to be used correctly", and inconsistency "can exacerbate the deterioration of the interface. The interfaces of stl containers are very consistent (although not perfectly consistent), which makes them very easy to use. For example, each STL container interface has a member function called size(), which will tell the caller how many objects are in the container at present.

In Java, length property is used to obtain the length of array, length method is used to obtain the length of String, and size method is used to obtain the size of List.

In. Net, there is a property named Length in its Array and Count in its List.

Don't ask users to remember that they must do something:

If any interface requires the user to remember to do something, it is easy to cause misuse, because the user may forget. For example, a factory function is imported in Clause 13, which returns a pointer to the derived class of Inv1estment, and then stores the pointer in the smart pointer shared_ptr to prevent resource leakage. However, the user may forget to store the pointer in the smart pointer So we can further optimize the factory function to directly return the smart pointer that stores the pointer to the derived class of Investment.

Investment* createInvestment();				//Factory function before optimization
shared_ptr<Investment> createInvestment();	//Optimized factory function

The advantage of using the smart pointer is that when the reference count is 0, the stored resources will be released automatically. If the release operation is not required, for example, if a function needs to be called, you can also specify the function to be called in the constructor of the smart pointer as the remover. This is good to ease the bad requirement of "requiring users to remember to do something".

Note:

  • A good interface is easy to use correctly and not easy to misuse.
  • ”Methods to promote the correct use of "include consistency of interfaces and compatibility with the behavior of built-in types
  • ”Ways to prevent misuse include creating new types, limiting operations on types, constraining object values, and eliminating user resource management responsibilities

Clause 19: design type

Posted by yacaph on Tue, 09 Nov 2021 13:39:28 -0800