概述
我们进行系统和功能程序设计时,应该有封装,继承,多态的基础概念,拿到需求的时候,时间足够的话,我建议使用UML去建模类图,这样可梳理设计和编码思路,因此下文还会介绍基本的类图关系,之后我们再一起讨论系统设计原则”SOLID“。
什么是对象
对象是指某个类的具体实现,面向对象编程是指把问题领域用实体的方式去建模,设计程序。它区别于面向过程编程,忽略具体的操作细节,把问题领域定位到实体上来。
我们用面向对象的思路去设计超市管理系统,关心并建模的主要应该是超市管理系统有哪些实体对象,这些实体可以进行什么操作。
用面向过程的思路去设计超市管理系统,更关心的是怎么去实现管理,具体的一步一步步骤需要明确,并分解成多个模块去单独实现,然后集成使用。
封装
封装是指对实体的属性和行为的范围界定。保留外部对类的访问接口。
减少耦合:可以独立地开发、测试、优化、使用、理解和修改
减轻维护的负担:可以更容易被理解,并且在调试的时候可以不影响其他模块有效地
调节性能:可以通过剖析来确定哪些模块影响了系统的性能提高软件的可重用性
降低了构建大型系统的风险:即使整个系统不可用,但是这些独立的模块却有可能是可用的
继承
继承是指extends,是is-a的关系,狗有狼狗,土狗之分,白人、黑人都是人,一般继承用于代码复用,一般案例选择"Interface-Abstract-Class"来实现,同一种东西的不同实现。
比如:定义一个Person接口里面有getName(),getLanguage(),在定义一个Abstract:"BasePerson"来实现Person接口,BasePerson仅实现了一个通用的方法getColor,具体的实现类WhitePerson,BlackPerson再去继承BasePerson,这样保证了代码的复用,又可以实现特有的属性。
继承应该遵循里式替换原则,也就是说子类的引用在保证运行结果一致的情况下可以代替父类引用。
多态
多态字面意思是:一个事物有多种状态。多态分为两种状态,编译时多态是重载,一个方法名有不同的实现,运行时多态是指一个接口的引用可以被多个实现类在运行时赋值,也就是一个接口的引用,的具体实现类在运行时才能确定。
UML建模工具
PlantText
泛化关系(Generalization)
表现为继承关系,extends, UML写法为 父类 <|-- 子类
实现关系(Realization)
表现为实现,implements, UML写法为 接口 <|.. 实现类
聚合关系(Aggregation)
可以看做是引用关系,部分是整体的一部分,但部分又可以单独存在。比如公司和员工的关系,公司不在了,但是员工本人还在的,表现为数据库里面就是,公司被删掉了,公司里面的员工id被删掉,但是员工本人的信息还在。
UML: 整体 o-- 部分
组合关系(Composition)
组合就是表面意思,1:n是指一个整体由多个部分组合而成,整体如果不存在了,他的各个部分也就不在了,比如:教学楼,公寓楼,操场,超市,图书馆构成了一个整体:学校。学校如果在数据标记为不可用,由它的各个部分也就不可用了。
UML: 整体 *-- 部分
关联关系(Association)
关联关系是A类包含了一个B类的属性,下图为A知道B,B不知道A。
UML表示为 A-->B
依赖关系(Dependency)
表现为A类中存在以B类为入参,局部变量。
UML表示为A ..> B
Solid设计原则
参考资料:
SOLID Principle and Explanation and examples SOLID五大设计原则
单一职责原则(Single Reponsibility Principle)
The Single-Responsibility Principle: A class should have only one reason to change.
单一职责是指应该尽量让类保持单一的功能。这个也有好处也有坏处,好处就是代码逻辑清晰,坏处就是拆分起来麻烦耗费时间。下面的列子拆分的感觉就是多此一举,但是逻辑代码清晰,所以具体的设计原则要看具体的业务规则来定义,sell是卖,sale是打折卖。
开闭原则 (The Close/Open Principle)
The Open/Closed Principle (OCP): Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification.
是指程序设计应该对扩展开放,对修改关闭。体现在代码上就是代码是可以扩展的,但是应该避免代码的改动。
典型的案例是queryTables根据传入的标识不同(a,b,c,d)查询不同的数据表(t1,t2,t3),简单的if.else可以快速完成开发任务,但是不符合The Close/Open Principle,原因在于当新增一个标识时,需要改动代码,而不是扩展代码。
优化策略:使用策略模式优化查询
里式替换原则(Liskov substitution principle)
More generally it states that objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program.
它的意思是父类对象应该可以被它的子类对象代替,并且不影响程序的正确性。一般用法是父类Abstract类实现子类的共有方法(行为一致),子类尽量不覆盖父类的方法,就可保证里式替换原则。
接口隔离原则(Interface Segregation Principle)
In programming, the interface segregation principle states that no client should be forced to depend on methods it does not use.
Put more simply: Do not add additional functionality to an existing interface by adding new methods.Instead, create a new interface and let your class implement multiple interfaces if needed.
意思是接口应该保持单一性,也就是不要合并多个请求到一个接口或者一个business.新增接口的时候,应该新建一个interface。
依赖反转原则(Dependency Inversion Principle)
High-level modules should not depend on low-level modules.Both should depend on abstractions.
Abstractions should not depend on details. Details should depend on abstractions.
意思是高层模块应该不依赖底层模块,而是依赖于抽象。抽象不依赖实现类,实现类应该依赖抽象。实现依赖反转一般用依赖注入(Dependency Injection )。
比如controller层对service层的依赖反转,我们之前没有依赖注入,controller层需要手动new service,然后对sevice实现类调用,高层依赖底层模块的实现,假如那一天sevice需要扩展另一个类似sevice,controller不得不修改代码。使用抽象可以把高层对底层的依赖转为底层和高层都对抽象的依赖。