Pimpl Idiom-------为完全隐藏类private实现细节的技巧。其实就是在公开的头文件的类中声明个私有的实现类指针。类似于Qt中的UI文件生成的ui_X.h文件。这个.h文件就是实现UI的头文件。 Use the pimpl idiom to keep implementation details out of your public header files. 就是上图。 When using the pimpl idiom use a private nested implementation class. Only use a public nested Impl class (or a public non-nested ...
Use the pimpl idiom to keep implementation details out of your public header files.引自 模式
就是上图。
When using the pimpl idiom use a private nested implementation class. Only use a public nested Impl class (or a public non-nested class) if other classes or free functions in the .cpp must access Impl members.
1. You can’t hide private virtual methods in the implementation class. These must appear in the public class so that any derived classes are able to override them.
2. You may need to add a pointer in the implementation class back to the public class so that the Impl class can call public methods. Although you could also pass the public class into the implementation class methods that need it.引自 模式
Make your class uncopyable.----------可以使用boost的noncopyable,实现机制实际上就是,对于拷贝构造函数声明为私有就可以了。
Explicitly define the copy semantics. ----------If you do want your users to be able to copy your pimpledobjects, then you should declare and define your own copy constructor and assignment operator.These can then perform a deep copy of your object, that is, create a copy of the Impl object instead of just copying the pointer.引自 模式
以下代码就是让类对象不可拷贝
#include <string>
class AutoTimer
{
public:
explicit AutoTimer(const std::string &name);
AutoTimer();
private:
// Make this object be non-copyable
AutoTimer(const AutoTimer &);
const AutoTimer &operator ¼(const AutoTimer &);
class Impl;
Impl *mImpl;
};
Think about the copy semantics of your pimpl classes and consider using a smart pointer to manage initialization and destruction of the implementation pointer.引自 模式
The primary disadvantage of the pimpl idiom is that you must now allocate and free an additional implementation object for every object that is created.
if you are concerned with the memory allocator performance, then you may consider using the “Fast Pimpl” idiom (Sutter, 1999) where you overload the new and delete operators for your Impl class to use a more efficient small-memory fixed-size allocator.引自 模式
单例模式
A Singleton is a more elegant way to maintain global state, but you should always question whether you need global state.
Declare the constructor, destructor, copy constructor, and assignment operator to be private (or protected) to enforce the Singleton property.引自 模式
Singleton &Singleton::GetInstance()
{
Mutex mutex;
ScopedLock(&mutex); // unlocks mutex on function exit
static Singleton instance;
return instance;
}
当然以上代码有个性能问题,就是每次调用获取实例的时候,Mutex都要检查,因为自旋锁需要一直等待,这样做可以更好:
A commonly proposed solution to optimize this kind of over aggressive locking behavior is to
use the Double Check Locking Pattern (DCLP)
Creating a thread-safe Singleton in C++ is hard. Consider initializing it with a static constructor or an API initialization function.引自 模式
单例模式VS依赖注入
Dependency injection is a technique where an object is passed into a class (injected) instead of having the class create and store the object itself.引自 模式
Dependency injection makes it easier to test code that uses Singletons.
Dependency injection can therefore be viewed as a way to avoid the proliferation of singletons by encouraging interfaces that accept the single instance as an input rather than requesting it internally via a GetInstance() method.
Consider using Monostate instead of Singleton if you don’t need lazy initialization of global data or if you want the singular nature of the class to be transparent.引自 模式
There are several alternatives to the Singleton pattern, including dependency injection, the Monostate pattern, and use of a session context.引自 模式
抽象工厂模型C++ 这个查百度 google就可以了。
Use Factory Methods to provide more powerful class construction semantics and to hide subclass details.引自 模式
API包装模式---有三种,Proxy, Adapter, and Facade
Writing a wrapper interface that sits on top of another set of classes is a relatively common API
design task.
perhaps you are working with a large legacy code base and rather than rearchitecting all of that code you decide to design a new cleaner API that hides the underlying legacy code (Feathers, 2004). Or perhaps you have written a C++ API and need to expose a plain C
interface for certain clients. Or perhaps you have a third-party library dependency that you want your clients to be able to access but you don’t want to expose that library directly to them.引自 模式
代理模型:
The Proxy design pattern (Figure 3.3) provides a one-to-one forwarding interface to another class: calling FunctionA() in the proxy class will cause it to call FunctionA() in the original class. 引自 模式
A Proxy provides an interface that forwards function calls to another interface of the same form.引自 模式
适配器模型
The Adapter design pattern (Figure 3.4) translates the interface for one class into a compatible but different interface. 引自 模式
外观模型
The Fac¸ade design pattern (Figure 3.5) presents a simplified interface for a larger collection of classes. In effect, it defines a higher-level interface that makes the underlying subsystem easier to use. 引自 模式
观察者模型
An Observer lets you decouple components and avoid cyclic dependencies.引自 模式
MVC模型
The MVC architectural pattern promotes the separation of core business logic, or the Model, from the user interface, or View. It also isolates the Controller logic that affects changes in the Model and updates the View.引自 模式
Functional requirements specify how your API should behave. Use cases describe the requirements for your API from the perspective of the user. Use cases can be simple lists of short goal-oriented descriptions or can be more formal structured specifications that follow a prescribed template. User stories are a way to capture minimal requirements from users within an agile development process. AP...
2015-08-20 10:13
Functional requirements specify how your API should behave.
Use cases describe the requirements for your API from the perspective of the user.
Use cases can be simple lists of short goal-oriented descriptions or can be more formal structured specifications that follow a prescribed template.
User stories are a way to capture minimal requirements from users within an agile development process.引自 设计
API设计
API design involves developing a top-level architecture and a detailed class hierarchy.引自 设计
架构设计
Architecture design is constrained by a multitude of unique organizational, environmental, and operational factors.
Always design for change. Change is inevitable.引自 设计
提取关键对象
Identifying the key objects for an API is difficult. Try looking at the problem from different perspectives and keep iterating and refining your model.引自 设计
架构模式
Avoid cyclic dependencies between components of your API.引自 设计
与架构交流
Describe the high-level architecture and design rationale for your API in the accompanying user documentation.引自 设计
类设计
Focus on designing 20% of the classes that define 80% of your API’s functionality.引自 设计
面向对象概念
使用继承
Avoid deep inheritance hierarchies.
Avoid multiple inheritance, except for interfaces and mixin classes.
The LSP states that it should always be possible to substitute a base class for a derived class without any change in behavior.
Prefer composition to inheritance.
Your API should be closed to incompatible changes in its interface, but open to extensibility of its functionality.
The Law of Demeter (LoD) states that you should only call functions in your own class or on immediately related objects.引自 设计
错误处理
1.返回错误码----一个数值,win32 API就是这样设计
2.抛异常
3.终止程序
Use a consistent and well-documented error handling mechanism.
Derive your own exceptions from std::exception.
Fail quickly and cleanly with accurate and thorough diagnostic details.引自 设计
松耦合 Good APIs exhibit loose coupling and high cohesion. 如下使用类前置声明就有效降低了类MyObject和类MyObjectHolder的耦合性 class MyObject; // only need to know the name of MyObject class MyObjectHolder { public: MyObjectHolder(); void SetObject(MyObject *obj); MyObject *GetObject() const; private: MyObject *mObj; }; In this case, if the associated .cpp file simply stores and returns the MyOb...
2015-07-23 10:49
松耦合
Good APIs exhibit loose coupling and high cohesion.引自 质量
如下使用类前置声明就有效降低了类MyObject和类MyObjectHolder的耦合性
class MyObject; // only need to know the name of MyObject
class MyObjectHolder
{
public:
MyObjectHolder();
void SetObject(MyObject *obj);
MyObject *GetObject() const;
private:
MyObject *mObj;
};
In this case, if the associated .cpp file simply stores and returns the MyObject pointer, and restricts any interaction with it to pointer comparisons, then it does not need to #include “MyObject.h” either. In that case, the MyObjectHolder class can be decoupled from the physical implementation of MyObject.
Use a forward declaration for a class unless you actually need to #include its full definition.引自 质量
降低类的耦合性
whenever you have a choice, you should prefer declaring a function as a non-member non-friend function rather than as a member function 。Doing so
improves encapsulation and also reduces the degree of coupling of those functions to the class.
Prefer using non-member non-friend functions instead of member functions to reduce coupling.引自 质量
Data redundancy can sometimes be justified to reduce coupling between classes.
Manager classes can reduce coupling by encapsulating several lower-level classes.引自 质量
回调,观察者和消息通知
reduce coupling within an API relates to the problem of notifying other classes when some event occurs.引自 质量
方便的API 也就是Core API应该是功能(参数)最小化的,只提供基本的操作。在Core层上在wrap一层以方便用户使用。 your API should present the basic functionality via an easy-to-use interface while reserving advanced functionality for a separate layer Add convenience APIs as separate modules or libraries that sit on top of your minimal core API. 易于使用 Avoiding the use of abbreviations can also play ...
your API should present the basic functionality via an easy-to-use interface while reserving advanced functionality for a separate layer
Add convenience APIs as separate modules or libraries that sit on top of your minimal core API.引自 质量
易于使用
Avoiding the use of abbreviations can also play a factor in discoverability
A good API, in addition to being easy to use, should also be difficult to misuse.
Prefer enums to booleans to improve code readability.
Avoid functions with multiple parameters of the same type.
Use consistent function naming and parameter ordering.
An orthogonal API means that functions do not have side effects.引自 质量
Two important factors to remember for designing orthogonal APIs are as follow.
1. Reduce redundancy. Ensure that the same information is not represented in more than one way.There should be a single authoritative source for each piece of knowledge.
2. Increase independence. Ensure that there is no overlapping of meaning in the concepts that are exposed. Any overlapping concepts should be decomposed into their basal components.引自 质量
Null dereferencing: trying to use -> or * operators on a NULL pointer.
Double freeing: calling delete or free() on a block of memory twice.
• Accessing invalid memory: trying to use -> or * operators on a pointer that has not been allocated yet or that has been freed already.
• Mixing Allocators: using delete to free memory that was allocated with malloc() or using free() to return memory allocated with new.
• Incorrect array deallocation: using the delete operator instead of delete [] to free an array.
• Memory leaks: not freeing a block of memory when you are finished with it.引自 质量
可以用智能指针避免以上的情况
1. Shared pointers. These are reference-counted pointers where the reference count can be incremented by one when a piece of code wants to hold onto the pointer and decremented by one when it is finished using the pointer. When the reference count reaches zero, the object pointed to by the pointer is automatically freed. This kind of pointer can help avoid the problems of accessing freed memory by ensuring that the pointer remains valid for the period that you wish to use it.
2. Weak pointers. A weak pointer contains a pointer to an object, normally a shared pointer, but does not contribute to the reference count for that object. If you have a shared pointer and a weak pointer referencing the same object, and the shared pointer is destroyed, the weak pointer immediately becomes NULL. In this way, weak pointers can detect whether the object being pointed to has expired: if the reference count for the object it is pointing to is zero. This helps avoid the dangling pointer problem where you can have a pointer that is referencing freed memory.
3. Scoped pointers. These pointers support ownership of single objects and automatically deallocate their objects when the pointer goes out of scope. They are sometimes also called auto pointers. Scoped pointers are defined as owning a single object and as such cannot be copied.
Return a dynamically allocated object using a smart pointer if the client is responsible for deallocating it.
Think of resource allocation and deallocation as object construction and destruction.引自 质量
平台独立性
Never put platform-specific #if or #ifdef statements into your public APIs. It exposes implementation details and makes your API appear different on different platforms.引自 质量
Data members of a class should always be declared private, never public or protected.
Validation.------You can perform validation on the values to ensure that the internal state of the class is always valid and consistent. For example, if you have a method that lets clients set a new RGB color, you could check that each of the supplied red, green, and blue values are within the valid range, for example, 0 to 255 or 0.0 to 1.0.
• Lazy evaluation-------Calculating the value of a variable may incur a significant cost, which youwould prefer to avoid until necessary. By using a getter method to access the underlying data value, you can defer the costly calculation until the value is actually requested.
• Caching.--------A classic optimization technique is to store the value of a frequently requested calculation and then directly return that value for future requests. For example, a machine’s total memory size can be found on Linux by parsing the /proc/meminfo file. Instead of performing a file read for every request to find the total memory size, it would be better to cache the result after
the first read and then simply return that cached value for future requests.
• Extra computation---------If necessary, you can perform additional operations whenever the client tries to access avariable.Forexample,perhapsyoualwayswanttowritethecurrentstateofaUserPreferences object to a configuration file on disk whenever the user changes the value of a preference setting.
• Notifications.--------Other modules may wish to know when a value has changed in your class. For example, if you are implementing a data model for a progress bar, the user interface code will want to know when the progress value has been updated so that it can update the GUI. You might therefore wish to issue a change notification as part of a setter method.
• Debugging.------You may want to add debugging or logging statements so that you can track when variables are accessed or changed by clients or you may wish to add assert statements to enforce assumptions.
Synchronization.-------You may release the first version of your API and then later find that you need to make it thread safe. The standard way to do this is to add mutex locking whenever a value is accessed.This would only be possible if you have wrapped access to the data values in getter/setter methods.
Finer access control.--------If you make a member variable public, then clients can read and write that value as they wish. However, by using getter/setter methods, you can provide a finer level of read/write control. For example, you can make the value be read-only by not providing a setter method.
•
Maintaining invariant relationships------Some internal data values may depend on each other. For example, in a car animation system you may calculate the velocity and acceleration of the car based on the time it takes to travel between key frames. You can calculate velocity based on the change in position over time, and acceleration based on the change in velocity over time. However, if a client can access your internal state for this calculation, they could change the acceleration
value so that it does not correlate to the car’s velocity, thus producing unexpected results.
引自 隐藏实现细节
First of all, the overhead
of a method call will very likely be insignificant for practically all of your API calls. Even if you are writing performance-critical APIs, the careful use of inlining, combined with a modern optimizing compiler, will normally completely eradicate the method call overhead, giving you all the performance benefits of directly exposing member variables. If you’re still concerned, try timing your API with inlined getter/setters and then with public member variables. 引自 隐藏实现细节
隐藏实现方法 Never return non-const pointers or references to private data members. This breaks encapsulation. 其实就是实现类公有接口的一些方法设置为private,或者把它们隐藏起来,不然API的用户可能会修改类的内部状态,也不要为一些关键的成员变量设置Get函数,导致可以随意操作类内部的状态。 考虑一段代码 #include <string> #include <stdio.h> #include <sys/socket.h> #include <unistd....
2015-07-22 10:08
隐藏实现方法
Never return non-const pointers or references to private data members. This breaks encapsulation.引自 质量
Obviously a better design for our URLDownloader class would be to make every method private,except for the constructor and the DownloadToFile() method. Everything else is implementation detail. You are then free to change the implementation without affecting any clients of the class.
There is still something very unsatisfying about this situation though. I have hidden the implementation details from the compiler’s point of view, but a person can still look at the header file and see all of the internal details for your class. In fact, this is very likely because you must distribute the header file to allow clients to compile their code against your API. Also, you must #include all header files needed for the private members of your class, even though they are not dependencies of the public interface. For example, the URLDownloader header needs to #include all of the platform-specific socket headers.
This is an unfortunate limitation of the C++ language: all public, protected, and private members of
a class must appear in the declaration for that class. Ideally, the header for our class would look like引自 质量
Then all of the private members could be declared somewhere else, such as in the .cpp file. However, this is not possible with C++ (the reason is so that the size of all objects can be known at compile time). Nevertheless, there are still ways to hide private members from your public header files (Headington, 1995). One popular technique is called the Pimpl idiom. This involves isolating all of a class’s private data members inside of a separate implementation class or struct in the .cpp file.
The .h file then only needs to contain an opaque pointer to this implementation class. 引自 质量
Prefer declaring private functionality as static functions within the .cpp file rather than exposing them in public headers as private methods. (Using the Pimpl idiom is even better though.)引自 质量
Remember Occam’s razor: “pluralitas non-est ponenda sine necessitate” (plurality should not be posited without
necessity).引自 质量
以上是奥科姆剃刀:若无必要,勿增实体。
you should try to keep your APIs as simple as you can: minimize the number of classes you expose and the number of public members in those classes. As a bonus, this will also make your API easier to understand, easier for your users to keep a mental model of the API in their heads, and easier for you to debug.
When in doubt, leave it out! Minimize the number of public classes and functions in your API.引自 质量
使用虚函数时需要注意的: Virtual function calls must be resolved at run time by performing a vtable lookup, whereas nonvirtual function calls can be resolved at compile time. This can make virtual function calls slower than non-virtual calls. In reality, this overhead may be negligible, particularly if your function does non-trivial work or if it is not called frequently. • The use of virtua...
2015-07-22 10:49
使用虚函数时需要注意的:
Virtual function calls must be resolved at run time by performing a vtable lookup, whereas nonvirtual function calls can be resolved at compile time. This can make virtual function calls slower
than non-virtual calls. In reality, this overhead may be negligible, particularly if your function
does non-trivial work or if it is not called frequently.
• The use of virtual functions increases the size of an object, typically by the size of a pointer to the
vtable. This may be an issue if you wish to create a small object that requires a very large number
of instances. Again, in practice this will likely be insignificant when compared to the amount of
memory consumed by your various member variables.
• Adding, reordering, or removing a virtual function will break binary compatibility. This is because a
virtual function call is typically represented as an integer offset into the vtable for the class. Therefore, changing its order or causing the order of any other virtual functions to change means that existing code will need to be recompiled to ensure that it still calls the right functions.
• Virtual functions cannot always be inlined. You may reasonably think that it does not make sense
to declare a virtual as inline at all because virtual functions are resolved at run time, whereas
inlining is a compile-time optimization. However, there are certain constrained situations where
a compiler can inline a virtual function. All the same, these are far fewer than the cases where a
non-virtual function can be inlined. (Remember that the inline keyword in C++ is merely a hint
to the compiler.)
Overloading is tricky with virtual functions. A symbol declared in a derived class will hide all
symbols with the same name in the base class. Therefore, a set of overloaded virtual functions
in a base class will be hidden by a single overriding function in a subclass. There are ways to
get around this (Dewhurst, 2002), but it’s better to simply avoid overloading virtual functions.
Avoid declaring functions as overridable (virtual) until you have a valid and compelling need to do so.
Ultimately, you should only allow overriding if you explicitly intend for this to be possible. A class with no virtual functions tends to be more robust and requires less maintenance than one with virtual functions. As a general rule of thumb, if your API does not call a particular method internally,then that method probably should not be virtual. You should also only allow subclassing in situations where it makes sense: where the potential subclasses form an “is-a” relationship with the base class.引自 质量
如果你想让你设计的类可以子类化,请遵守以下原则:
1. Always declare your destructor to be virtual if there are any virtual functions in your class. This is so that subclasses can free any extra resources that they may have allocated.
2. Always document how methods of your class call each other. If a client wants to provide an alternative implementation for a virtual function, they will need to know which other methods need to be called within their implementation in order to preserve the internal integrity of the object.
3. Never call virtual functions from your constructor or destructor. These calls will never be directed to a subclass (Meyers, 2005). This does not affect the appearance of your API, but it’s a good rule to know all the same.引自 质量
Functional requirements specify how your API should behave. Use cases describe the requirements for your API from the perspective of the user. Use cases can be simple lists of short goal-oriented descriptions or can be more formal structured specifications that follow a prescribed template. User stories are a way to capture minimal requirements from users within an agile development process. AP...
2015-08-20 10:13
Functional requirements specify how your API should behave.
Use cases describe the requirements for your API from the perspective of the user.
Use cases can be simple lists of short goal-oriented descriptions or can be more formal structured specifications that follow a prescribed template.
User stories are a way to capture minimal requirements from users within an agile development process.引自 设计
API设计
API design involves developing a top-level architecture and a detailed class hierarchy.引自 设计
架构设计
Architecture design is constrained by a multitude of unique organizational, environmental, and operational factors.
Always design for change. Change is inevitable.引自 设计
提取关键对象
Identifying the key objects for an API is difficult. Try looking at the problem from different perspectives and keep iterating and refining your model.引自 设计
架构模式
Avoid cyclic dependencies between components of your API.引自 设计
与架构交流
Describe the high-level architecture and design rationale for your API in the accompanying user documentation.引自 设计
类设计
Focus on designing 20% of the classes that define 80% of your API’s functionality.引自 设计
面向对象概念
使用继承
Avoid deep inheritance hierarchies.
Avoid multiple inheritance, except for interfaces and mixin classes.
The LSP states that it should always be possible to substitute a base class for a derived class without any change in behavior.
Prefer composition to inheritance.
Your API should be closed to incompatible changes in its interface, but open to extensibility of its functionality.
The Law of Demeter (LoD) states that you should only call functions in your own class or on immediately related objects.引自 设计
错误处理
1.返回错误码----一个数值,win32 API就是这样设计
2.抛异常
3.终止程序
Use a consistent and well-documented error handling mechanism.
Derive your own exceptions from std::exception.
Fail quickly and cleanly with accurate and thorough diagnostic details.引自 设计
Pimpl Idiom-------为完全隐藏类private实现细节的技巧。其实就是在公开的头文件的类中声明个私有的实现类指针。类似于Qt中的UI文件生成的ui_X.h文件。这个.h文件就是实现UI的头文件。 Use the pimpl idiom to keep implementation details out of your public header files. 就是上图。 When using the pimpl idiom use a private nested implementation class. Only use a public nested Impl class (or a public non-nested ...
Use the pimpl idiom to keep implementation details out of your public header files.引自 模式
就是上图。
When using the pimpl idiom use a private nested implementation class. Only use a public nested Impl class (or a public non-nested class) if other classes or free functions in the .cpp must access Impl members.
1. You can’t hide private virtual methods in the implementation class. These must appear in the public class so that any derived classes are able to override them.
2. You may need to add a pointer in the implementation class back to the public class so that the Impl class can call public methods. Although you could also pass the public class into the implementation class methods that need it.引自 模式
Make your class uncopyable.----------可以使用boost的noncopyable,实现机制实际上就是,对于拷贝构造函数声明为私有就可以了。
Explicitly define the copy semantics. ----------If you do want your users to be able to copy your pimpledobjects, then you should declare and define your own copy constructor and assignment operator.These can then perform a deep copy of your object, that is, create a copy of the Impl object instead of just copying the pointer.引自 模式
以下代码就是让类对象不可拷贝
#include <string>
class AutoTimer
{
public:
explicit AutoTimer(const std::string &name);
AutoTimer();
private:
// Make this object be non-copyable
AutoTimer(const AutoTimer &);
const AutoTimer &operator ¼(const AutoTimer &);
class Impl;
Impl *mImpl;
};
Think about the copy semantics of your pimpl classes and consider using a smart pointer to manage initialization and destruction of the implementation pointer.引自 模式
The primary disadvantage of the pimpl idiom is that you must now allocate and free an additional implementation object for every object that is created.
if you are concerned with the memory allocator performance, then you may consider using the “Fast Pimpl” idiom (Sutter, 1999) where you overload the new and delete operators for your Impl class to use a more efficient small-memory fixed-size allocator.引自 模式
单例模式
A Singleton is a more elegant way to maintain global state, but you should always question whether you need global state.
Declare the constructor, destructor, copy constructor, and assignment operator to be private (or protected) to enforce the Singleton property.引自 模式
Singleton &Singleton::GetInstance()
{
Mutex mutex;
ScopedLock(&mutex); // unlocks mutex on function exit
static Singleton instance;
return instance;
}
当然以上代码有个性能问题,就是每次调用获取实例的时候,Mutex都要检查,因为自旋锁需要一直等待,这样做可以更好:
A commonly proposed solution to optimize this kind of over aggressive locking behavior is to
use the Double Check Locking Pattern (DCLP)
Creating a thread-safe Singleton in C++ is hard. Consider initializing it with a static constructor or an API initialization function.引自 模式
单例模式VS依赖注入
Dependency injection is a technique where an object is passed into a class (injected) instead of having the class create and store the object itself.引自 模式
Dependency injection makes it easier to test code that uses Singletons.
Dependency injection can therefore be viewed as a way to avoid the proliferation of singletons by encouraging interfaces that accept the single instance as an input rather than requesting it internally via a GetInstance() method.
Consider using Monostate instead of Singleton if you don’t need lazy initialization of global data or if you want the singular nature of the class to be transparent.引自 模式
There are several alternatives to the Singleton pattern, including dependency injection, the Monostate pattern, and use of a session context.引自 模式
抽象工厂模型C++ 这个查百度 google就可以了。
Use Factory Methods to provide more powerful class construction semantics and to hide subclass details.引自 模式
API包装模式---有三种,Proxy, Adapter, and Facade
Writing a wrapper interface that sits on top of another set of classes is a relatively common API
design task.
perhaps you are working with a large legacy code base and rather than rearchitecting all of that code you decide to design a new cleaner API that hides the underlying legacy code (Feathers, 2004). Or perhaps you have written a C++ API and need to expose a plain C
interface for certain clients. Or perhaps you have a third-party library dependency that you want your clients to be able to access but you don’t want to expose that library directly to them.引自 模式
代理模型:
The Proxy design pattern (Figure 3.3) provides a one-to-one forwarding interface to another class: calling FunctionA() in the proxy class will cause it to call FunctionA() in the original class. 引自 模式
A Proxy provides an interface that forwards function calls to another interface of the same form.引自 模式
适配器模型
The Adapter design pattern (Figure 3.4) translates the interface for one class into a compatible but different interface. 引自 模式
外观模型
The Fac¸ade design pattern (Figure 3.5) presents a simplified interface for a larger collection of classes. In effect, it defines a higher-level interface that makes the underlying subsystem easier to use. 引自 模式
观察者模型
An Observer lets you decouple components and avoid cyclic dependencies.引自 模式
MVC模型
The MVC architectural pattern promotes the separation of core business logic, or the Model, from the user interface, or View. It also isolates the Controller logic that affects changes in the Model and updates the View.引自 模式
松耦合 Good APIs exhibit loose coupling and high cohesion. 如下使用类前置声明就有效降低了类MyObject和类MyObjectHolder的耦合性 class MyObject; // only need to know the name of MyObject class MyObjectHolder { public: MyObjectHolder(); void SetObject(MyObject *obj); MyObject *GetObject() const; private: MyObject *mObj; }; In this case, if the associated .cpp file simply stores and returns the MyOb...
2015-07-23 10:49
松耦合
Good APIs exhibit loose coupling and high cohesion.引自 质量
如下使用类前置声明就有效降低了类MyObject和类MyObjectHolder的耦合性
class MyObject; // only need to know the name of MyObject
class MyObjectHolder
{
public:
MyObjectHolder();
void SetObject(MyObject *obj);
MyObject *GetObject() const;
private:
MyObject *mObj;
};
In this case, if the associated .cpp file simply stores and returns the MyObject pointer, and restricts any interaction with it to pointer comparisons, then it does not need to #include “MyObject.h” either. In that case, the MyObjectHolder class can be decoupled from the physical implementation of MyObject.
Use a forward declaration for a class unless you actually need to #include its full definition.引自 质量
降低类的耦合性
whenever you have a choice, you should prefer declaring a function as a non-member non-friend function rather than as a member function 。Doing so
improves encapsulation and also reduces the degree of coupling of those functions to the class.
Prefer using non-member non-friend functions instead of member functions to reduce coupling.引自 质量
Data redundancy can sometimes be justified to reduce coupling between classes.
Manager classes can reduce coupling by encapsulating several lower-level classes.引自 质量
回调,观察者和消息通知
reduce coupling within an API relates to the problem of notifying other classes when some event occurs.引自 质量
方便的API 也就是Core API应该是功能(参数)最小化的,只提供基本的操作。在Core层上在wrap一层以方便用户使用。 your API should present the basic functionality via an easy-to-use interface while reserving advanced functionality for a separate layer Add convenience APIs as separate modules or libraries that sit on top of your minimal core API. 易于使用 Avoiding the use of abbreviations can also play ...
your API should present the basic functionality via an easy-to-use interface while reserving advanced functionality for a separate layer
Add convenience APIs as separate modules or libraries that sit on top of your minimal core API.引自 质量
易于使用
Avoiding the use of abbreviations can also play a factor in discoverability
A good API, in addition to being easy to use, should also be difficult to misuse.
Prefer enums to booleans to improve code readability.
Avoid functions with multiple parameters of the same type.
Use consistent function naming and parameter ordering.
An orthogonal API means that functions do not have side effects.引自 质量
Two important factors to remember for designing orthogonal APIs are as follow.
1. Reduce redundancy. Ensure that the same information is not represented in more than one way.There should be a single authoritative source for each piece of knowledge.
2. Increase independence. Ensure that there is no overlapping of meaning in the concepts that are exposed. Any overlapping concepts should be decomposed into their basal components.引自 质量
Null dereferencing: trying to use -> or * operators on a NULL pointer.
Double freeing: calling delete or free() on a block of memory twice.
• Accessing invalid memory: trying to use -> or * operators on a pointer that has not been allocated yet or that has been freed already.
• Mixing Allocators: using delete to free memory that was allocated with malloc() or using free() to return memory allocated with new.
• Incorrect array deallocation: using the delete operator instead of delete [] to free an array.
• Memory leaks: not freeing a block of memory when you are finished with it.引自 质量
可以用智能指针避免以上的情况
1. Shared pointers. These are reference-counted pointers where the reference count can be incremented by one when a piece of code wants to hold onto the pointer and decremented by one when it is finished using the pointer. When the reference count reaches zero, the object pointed to by the pointer is automatically freed. This kind of pointer can help avoid the problems of accessing freed memory by ensuring that the pointer remains valid for the period that you wish to use it.
2. Weak pointers. A weak pointer contains a pointer to an object, normally a shared pointer, but does not contribute to the reference count for that object. If you have a shared pointer and a weak pointer referencing the same object, and the shared pointer is destroyed, the weak pointer immediately becomes NULL. In this way, weak pointers can detect whether the object being pointed to has expired: if the reference count for the object it is pointing to is zero. This helps avoid the dangling pointer problem where you can have a pointer that is referencing freed memory.
3. Scoped pointers. These pointers support ownership of single objects and automatically deallocate their objects when the pointer goes out of scope. They are sometimes also called auto pointers. Scoped pointers are defined as owning a single object and as such cannot be copied.
Return a dynamically allocated object using a smart pointer if the client is responsible for deallocating it.
Think of resource allocation and deallocation as object construction and destruction.引自 质量
平台独立性
Never put platform-specific #if or #ifdef statements into your public APIs. It exposes implementation details and makes your API appear different on different platforms.引自 质量
0 有用 瑞士菜刀 2013-11-14
大概是由于期待本较高的缘故,感觉说得比较泛泛。
0 有用 Douglas 2013-12-20
工具
0 有用 探寻可能 2019-06-23
前五章推荐,后面感觉就是Meyers的翻版
0 有用 锦帆贼 2015-11-07
匆匆浏览了一遍,理论性的东西太多
0 有用 装作有方向 2018-01-19
全面,很多实用经验。 可以作为一个大纲性质的书来读,通过这个可以增加广度。
0 有用 蓝白蛙 2019-10-19
一些设计理念。
0 有用 探寻可能 2019-06-23
前五章推荐,后面感觉就是Meyers的翻版
0 有用 冬瓜兄弟 2019-03-21
多数在其他书中都有介绍,无需再买
0 有用 青离 2019-01-06
1. code 部分以封装为核心,从模式、风格、语言特性等方面展开,有条有理,细节翔实。2. beyond code 部分乃经验所积累,探讨若干主观性因素。3. 代码若为食材,文档则是调料,不可不慎。
0 有用 Yotsuba 2018-12-04
讲得比较浅。前面部分基本都在effective系列的书看过。后面软件工程部分翻了翻,就过去了。