[转帖]A summary of OO Principles

本文介绍面向对象编程的主要规则,旨在启发读者思考。阐述了面向对象设计的目标是应对软件需求变化,还列举了依赖倒置、开闭、李氏代换等多个原则,以及启发式方法和约定,如成员变量私有、无全局变量等,以构建稳定可维护的代码。

Introduction

OO Design is more than just using an OO language. Over the years many bright programmers have built up a collection of rules that help to build well designed maintainable code. This article lists the main rules of OO programming. The intention is to inspire the reader to think about these rules and make further reading. There is a lot of material on the web that drills down into more details with plenty of examples. 

Ivar Jacabson said ”All systems change during their life cycles. This must be borne in mind when developing systems expected to last longer than the first version”. In other words software requirements change with time. The goal of Object Orientated Design is to program in such a way that such changes to the software are predictable and do not make a large impact in the program. In other words it should be stable in the presence of change 

Bad design is characterized by 

A single change affects many other parts of the system (Rigidity) 
A single change affects unexpected parts of the system (Fragility) 
It is hard to reuse in another application (Immobility) 
The Dependency Inversion Principle DIP(依赖倒置原则)

Imagine you have a simple database program. You don’t want to change the entire application when changing the database. This principle is targeted at removing such unwanted interdependency that can cause a design to be fragile. The rule states

High level modules should not depend upon low level modules. Both should depend upon abstractions 
Abstractions should not depend upon details. Details should depend upon abstractions 
Booch said “All well structured object orientated architectures have clearly-defined layers with each layer providing some coherent set of services through a well defined and controlled interface.”

In other words design applications in layers where high level layers call lower level layers using Abstract interfaces. To conform to the principle of dependency inversion, we must isolate abstraction from the details of the problem. Then we must direct the dependencies of the design upon the abstractions.

Good dependencies are extremely unlikely to change. In other words they are stable. We would like to base our architectural design around stable, non-volatile modules.

The Open-Close Principle OCP(开闭原则)

Software entities (Classes, Modules, functions etc) should be open for extension, but closed for modification

In other words design classes that never change. When a new requirements come add new code and don’t edit existing code. It is not possible to close against all possible changes. Therefore an experienced developer needs to understand the possible future wishes of users in order to make Strategic Closure. There are two ways of closure:

Using Abstraction to gain explicit closure - This means the programmer applies abstraction to those parts of the programmer the designer feels are subject to change. 
Using Data Driven Approach to achieve closure 
Liskov Substitution Principle LSP (李氏代换原则)

Every function that operates upon a reference or pointer to a base class should be able to operate on derivatives of that base class without knowing it. This means that virtual member functions of the derived class must expect only all the corresponding member functions of the base class. In other words any function that uses a base class must not be confused when a derived class is substituted for the base class

This is a difficult principle to apply. To conform avoid overwriting base class functions because this involves programming with details, instead try to program in abstractions

If this is violated then functions that operate on the pointers must first check the type of the actual object in order to work correctly.

Heuristics and Conventions

Make all member variables Private: Otherwise no function that calls the class can be closed to change. For example a status variable can change from Boolean to an enumeration, if this is not handled as a property then we cannot close status. This is called encapsulation.

No Global Variables. Because misbehaving modules may write erroneous data to such global variables whose effect can be felt in many places throughout the program. Sometimes Global variables are useful e.g. cout and cin in c++. If they do not violate the open close principle then sometimes they are worth the style violation

Stability Dependencies Principle SDP(稳定依赖原则)

The dependencies between packages in a design should be in the direction of stability of the packages. A package should only depend upon packages that are more stable than it is.

Some volatility is necessary if the design is to be maintained. This is achieved by using the Common Closure Principle, in this way we design packages to be volatile and we expect them to change. Any package that we expect to be volatile should not be depended upon by a package that is difficult to change.

Some things we don’t want to change. For example architectural decisions should be stable and not at all volatile. Therefore classes that encapsulate the high level design should be stable.

The stable Abstractions Principle SAP(稳定抽象原则)

Packages that are maximally stable should be maximally abstract. Instable packages should be concrete. The abstraction of a package should be in proportion to it's stability

Common Reuse Principle CRP(共同通用原则)

If you reuse one class of a package, you reuse them all. This because any package delivered contains a released set of classes, therefore a change in any class means a new release of the entire package.

The Reuse / Release Equivalence Principle REP(重用发布等价原则)

The granule of reuse is the granule of release. Only components that are released through a tracking system can be effectively reused. This principle is important when there are several teams working on an application. To avoid one team disrupting another all packages used are tested and released. In this way the introduction of modified packages is in a controlled way.

The Common Closure Principle CCP(共同封闭原则)

The classes in a package should be closed together against the same kinds of changes. Any change in a package affects all classes in the package. Just like a well organized team has a common goal because they all have to work together. This principle means that you should have a common strategic closure concept used through all classes in a package because they have to be released all together.

The stability Dependencies Principle SDP(稳定依赖原则)

The dependencies between packages in a design should be in the direction of stability of the packages. A package should only depend upon packages that are more stable than it is. 

Designs that are highly interdependent tend to be rigid, not reusable and hard to maintain

The Acyclic Dependencies Principle (ADP) (吴环依赖原则)

The dependency structure between package must be a Direct Acyclic Graph (DAG). This means that if you plot out all packages it should be possible to arrange the dependencies to always point from top to bottom. Also it should not be possible to follow any lines of dependence and end up back at the same package. Because such packages would have to be released all at the same time defeating the object of having them as separate packages

The Interface Segregation Principle ISP(接口隔离原则)

Clients should not be forced to depend upon interfaces that they don’t use.

This principle deals with the disadvantages of fat interfaces. Fat interfaces are not cohesive. In other words the interfaces of classes should be broken into groups of member functions. Each groups servers a different set of clients. Separation can be achieved by:

Separation through Delegation 
Separation through multiple inheritance 
If this principle is violated then there is a coupling between all clients 

Polyad vs Monad

Monad is when properties are grouped into 1 single object that is then passed in a function parameter. Unfortunately this brings a dependency across all properties in that single object. Therefore its better to pass smaller objects (Polyad), in this way the dependencies are broken into smaller groups.

Interface Pollution

As we build up classes there is a tendency for us to add functionality that is specific for a particular implementation. In this way the interface gets populated by functions and properties that are not required if the class was in a different context, thus making the interface fat. In this way this violates the Liskov Substitution principle. Separate Clients means separate interfaces

There is a backward force applied by clients upon interfaces. For example a user may wish to add a trivial extra function that cannot be exactly positioned in existing interfaces. 
面向对象编程(Object-Oriented Programming, OOP)的四大基本原则(Four Pillars of OOP)是封装(Encapsulation)、抽象(Abstraction)、继承(Inheritance)和多态(Polymorphism)。这些原则构成了OOP的核心思想,旨在提高代码的可维护性、可扩展性和重用性。 ### 封装(Encapsulation) 封装是指将数据(属性)和行为(方法)捆绑在一起,并限制对内部状态的直接访问。通过使用访问修饰符(如 `private`、`protected` 和 `public`),可以控制类成员的可见性,从而保护对象的状态不被外部随意修改。封装有助于实现数据隐藏,提高安全性与模块化。 例如,在Java中: ```java public class Person { private String name; // 私有属性 public String getName() { return name; } public void setName(String name) { this.name = name; } } ``` 该类的 `name` 属性被封装为私有,只能通过公共方法 `getName()` 和 `setName()` 进行访问和修改[^1]。 ### 抽象(Abstraction) 抽象是指隐藏复杂的实现细节,仅向外部暴露必要的接口。通过抽象,用户无需了解对象内部如何工作,只需知道如何与对象交互。这有助于简化复杂系统的使用,并减少耦合度。 例如,一个汽车类可能提供 `start()` 和 `stop()` 方法,而隐藏发动机启动和停止的具体逻辑[^1]。 ### 继承(Inheritance) 继承允许一个类(子类或派生类)继承另一个类(父类或基类)的属性和方法。通过继承,可以实现代码重用,并建立类之间的层次关系。继承支持“is-a”关系,例如 `Dog` 是 `Animal` 的一种。 例如,在Java中: ```java class Animal { void eat() { System.out.println("This animal eats food"); } } class Dog extends Animal { void bark() { System.out.println("The dog barks"); } } ``` `Dog` 类继承了 `Animal` 类的 `eat()` 方法,并添加了自己的 `bark()` 方法[^1]。 ### 多态(Polymorphism) 多态是指一个对象可以以多种形式存在。多态通常通过方法重写(Override)实现,允许子类重新定义父类的方法。运行时多态(动态绑定)使得程序可以在运行时根据对象的实际类型调用相应的方法。 例如: ```java class Animal { void sound() { System.out.println("Animal makes a sound"); } } class Dog extends Animal { @Override void sound() { System.out.println("Dog barks"); } } class Cat extends Animal { @Override void sound() { System.out.println("Cat meows"); } } ``` 在运行时,`Animal` 类型的引用可以根据实际对象类型调用 `sound()` 的不同实现[^1]。 这些四大原则共同构成了面向对象编程的基础,帮助开发者构建结构清晰、易于维护和扩展的软件系统。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值