面向对象设计(OOD)设计原则

463 阅读6分钟

开闭原则

优点

实践开闭原则的优点在于可以在不改动原有代码的前提下给程序扩展功能。增加了程序的可扩展性,同时也降低了程序的维护成本。

需求点

由于教学资源有限,开始的时候只有类似于博客的,通过文字讲解的课程。 但是随着教学资源的增多,后来增加了视频课程,音频课程以及直播课程。

不好的设计

//================== Course.h ==================

@interface Course : NSObject

@property (nonatomic, copy) NSString *courseTitle;         //课程名称
@property (nonatomic, copy) NSString *courseIntroduction;  //课程介绍
@property (nonatomic, copy) NSString *teacherName;         //讲师姓名
@property (nonatomic, copy) NSString *content;             //文字内容


//新需求:视频课程
@property (nonatomic, copy) NSString *videoUrl;

//新需求:音频课程
@property (nonatomic, copy) NSString *audioUrl;

//新需求:直播课程
@property (nonatomic, copy) NSString *liveUrl;

@end

较好的设计

//================== Course.h ==================

@interface Course : NSObject

@property (nonatomic, copy) NSString *courseTitle;         //课程名称
@property (nonatomic, copy) NSString *courseIntroduction;  //课程介绍
@property (nonatomic, copy) NSString *teacherName;         //讲师姓名

@end

文字课程类

@interface TextCourse : Course

@property (nonatomic, copy) NSString *content;             //文字内容

@end

视频课程类

@interface VideoCourse : Course

@property (nonatomic, copy) NSString *videoUrl;            //视频地址

@end

音频课程类

@interface AudioCourse : Course

@property (nonatomic, copy) NSString *audioUrl;            //音频地址

@end

单一职责原则

优点

如果类与方法的职责划分得很清晰,不但可以提高代码的可读性,更实际性地更降低了程序出错的风险,因为清晰的代码会让bug无处藏身,也有利于bug的追踪,也就是降低了程序的维护成本。

需求

初始需求:需要创造一个员工类,这个类有员工的一些基本信息。

新需求:增加两个方法:

  • 判定员工在今年是否升职
  • 计算员工的薪水

不好的设计

@interface Employee : NSObject

//============ 初始需求 ============
@property (nonatomic, copy) NSString *name;       //员工姓名
@property (nonatomic, copy) NSString *address;    //员工住址
@property (nonatomic, copy) NSString *employeeID; //员工ID



//============ 新需求 ============
//计算薪水
- (double)calculateSalary;

//今年是否晋升
- (BOOL)willGetPromotionThisYear;

@end

较好的设计

@interface Employee : NSObject

//初始需求
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *address;
@property (nonatomic, copy) NSString *employeeID;

@end

会计部门

#import "Employee.h"

//会计部门类
@interface FinancialApartment : NSObject

//计算薪水
- (double)calculateSalary:(Employee *)employee;

@end

人事部门

#import "Employee.h"

//人事部门类
@interface HRApartment : NSObject

//今年是否晋升
- (BOOL)willGetPromotionThisYear:(Employee*)employee;

@end

依赖倒置原则

优点

通过抽象来搭建框架,建立类和类的关联,以减少类间的耦合性。而且以抽象搭建的系统要比以具体实现搭建的系统更加稳定,扩展性更高,同时也便于维护。

需求

前端和后端开发人员开发同一个项目。 新需求:

  • 后端的开发语言改成GO语言
  • 后期老板要求做移动端的APP(需要iOS和安卓的开发者)

不好的设计

前端开发者

@interface FrondEndDeveloper : NSObject

- (void)writeJavaScriptCode;

@end



//================== FrondEndDeveloper.m ==================

@implementation FrondEndDeveloper

- (void)writeJavaScriptCode{
    NSLog(@"Write JavaScript code");
}

@end

后端开发者


@interface BackEndDeveloper : NSObject

- (void)writeJavaCode;

@end



//================== BackEndDeveloper.m ==================

@implementation BackEndDeveloper

- (void)writeJavaCode{
    NSLog(@"Write Java code");
}
@end

Project类

//================== Project.h ==================

@interface Project : NSObject

//构造方法,传入开发者的数组
- (instancetype)initWithDevelopers:(NSArray *)developers;

//开始开发
- (void)startDeveloping;

@end



//================== Project.m ==================

#import "Project.h"
#import "FrondEndDeveloper.h"
#import "BackEndDeveloper.h"

@implementation Project
{
    NSArray *_developers;
}


- (instancetype)initWithDevelopers:(NSArray *)developers{

    if (self = [super init]) {
        _developers = developers;
    }
    return self;
}



- (void)startDeveloping{

    [_developers enumerateObjectsUsingBlock:^(id  _Nonnull developer, NSUInteger idx, BOOL * _Nonnull stop) {

        if ([developer isKindOfClass:[FrondEndDeveloper class]]) {

            [developer writeJavaScriptCode];

        }else if ([developer isKindOfClass:[BackEndDeveloper class]]){

            [developer writeJavaCode];

        }else{
            //no such developer
        }
    }];
}

@end

较好的设计

@protocol DeveloperProtocol <NSObject>

- (void)writeCode;

@end

//================== FrondEndDeveloper.h ==================

@interface FrondEndDeveloper : NSObject<DeveloperProtocol>
@end



//================== FrondEndDeveloper.m ==================

@implementation FrondEndDeveloper

- (void)writeCode{
    NSLog(@"Write JavaScript code");
}
@end

//================== BackEndDeveloper.h ==================

@interface BackEndDeveloper : NSObject<DeveloperProtocol>
@end



//================== BackEndDeveloper.m ==================
@implementation BackEndDeveloper

- (void)writeCode{
    NSLog(@"Write Java code");
}
@end

//================== Project.h ==================

#import "DeveloperProtocol.h"

@interface Project : NSObject

//只需传入遵循DeveloperProtocol的对象数组即可
- (instancetype)initWithDevelopers:(NSArray <id <DeveloperProtocol>>*)developers;

//开始开发
- (void)startDeveloping;

@end


//================== Project.m ==================

#import "FrondEndDeveloper.h"
#import "BackEndDeveloper.h"

@implementation Project
{
    NSArray <id <DeveloperProtocol>>* _developers;
}


- (instancetype)initWithDevelopers:(NSArray <id <DeveloperProtocol>>*)developers{

    if (self = [super init]) {
        _developers = developers;
    }
    return self;

}


- (void)startDeveloping{

    //每次循环,直接向对象发送writeCode方法即可,不需要判断
    [_developers enumerateObjectsUsingBlock:^(id<DeveloperProtocol>  _Nonnull developer, NSUInteger idx, BOOL * _Nonnull stop) {

        [developer writeCode];
    }];

}

@end

接口分离原则

优点

避免同一个接口(协议)里面包含不同类职责的方法,接口责任划分更加明确,符合高内聚低耦合的思想。(参考UITableviewDelegate,UItableviewDataSource)

需求

现在的餐厅除了提供传统的店内服务,多数也都支持网上下单,网上支付功能。写一些接口方法来涵盖餐厅的所有的下单及支付功能。

不好的设计

//================== RestaurantProtocol.h ==================

@protocol RestaurantProtocol <NSObject>

- (void)placeOnlineOrder;         //下订单:online
- (void)placeTelephoneOrder;      //下订单:通过电话
- (void)placeWalkInCustomerOrder; //下订单:在店里

- (void)payOnline;                //支付订单:online
- (void)payInPerson;              //支付订单:在店里支付

@end

较好的设计

下单协议

@protocol RestaurantPlaceOrderProtocol <NSObject>

- (void)placeOrder;

@end

支付协议

@protocol RestaurantPaymentProtocol <NSObject>

- (void)payOrder;

@end

迪米特法则

优点

实践迪米特法则可以良好地降低类与类之间的耦合,减少类与类之间的关联程度,让类与类之间的协作更加直接。

需求

设计一个汽车类,包含汽车的品牌名称,引擎等成员变量。提供一个方法返回引擎的品牌名称。

不好的设计

//================== Car.h ==================

@class GasEngine;

@interface Car : NSObject

//构造方法
- (instancetype)initWithEngine:(GasEngine *)engine;

//返回私有成员变量:引擎的实例
- (GasEngine *)usingEngine;

@end




//================== Car.m ==================

#import "Car.h"
#import "GasEngine.h"

@implementation Car
{
    GasEngine *_engine;
}

- (instancetype)initWithEngine:(GasEngine *)engine{

    self = [super init];

    if (self) {
        _engine = engine;
    }
    return self;
}

- (GasEngine *)usingEngine{

    return _engine;
}

@end

//================== GasEngine.h ==================
@interface GasEngine : NSObject

@property (nonatomic, copy) NSString *brandName;

@end

//================== Client.m ==================

#import "GasEngine.h"
#import "Car.h"

- (NSString *)findCarEngineBrandName:(Car *)car{

    GasEngine *engine = [car usingEngine];
    NSString *engineBrandName = engine.brandName;//获取到了引擎的品牌名称
    return engineBrandName;
}

较好的设计

//================== Car.h ==================

@class GasEngine;

@interface Car : NSObject

//构造方法
- (instancetype)initWithEngine:(GasEngine *)engine;

//直接返回引擎品牌名称
- (NSString *)usingEngineBrandName;

@end


//================== Car.m ==================

#import "Car.h"
#import "GasEngine.h"

@implementation Car
{
    GasEngine *_engine;
}

- (instancetype)initWithEngine:(GasEngine *)engine{

    self = [super init];

    if (self) {
        _engine = engine;
    }
    return self;
}


- (NSString *)usingEngineBrandName{
    return _engine.brand;
}

@end

//================== Client.m ==================

#import "Car.h"

- (NSString *)findCarEngineBrandName:(Car *)car{

    NSString *engineBrandName = [car usingEngineBrandName]; //直接获取到了引擎的品牌名称
    return engineBrandName;
}

里氏替换原则

优点

可以检验继承使用的正确性,约束继承在使用上的泛滥。

需求

创建两个类:长方形和正方形,都可以设置宽高(边长),也可以输出面积大小。

不好的设计

//================== Rectangle.h ==================

@interface Rectangle : NSObject
{
@protected double _width;
@protected double _height;
}

//设置宽高
- (void)setWidth:(double)width;
- (void)setHeight:(double)height;

//获取宽高
- (double)width;
- (double)height;

//获取面积
- (double)getArea;

@end



//================== Rectangle.m ==================

@implementation Rectangle

- (void)setWidth:(double)width{
    _width = width;
}

- (void)setHeight:(double)height{
    _height = height;
}

- (double)width{
    return _width;
}

- (double)height{
    return _height;
}


- (double)getArea{
    return _width * _height;
}

@end

@interface Square : Rectangle
@end



//================== Square.m ==================

@implementation Square

- (void)setWidth:(double)width{

    _width = width;
    _height = width;
}

- (void)setHeight:(double)height{

    _width = height;
    _height = height;
}

@end

较好的设计

四边形

//================== Quadrangle.h ==================

@interface Quadrangle : NSObject
{
@protected double _width;
@protected double _height;
}

- (void)setWidth:(double)width;
- (void)setHeight:(double)height;

- (double)width;
- (double)height;

- (double)getArea;
@end

长方形

//================== Rectangle.h ==================

#import "Quadrangle.h"

@interface Rectangle : Quadrangle

@end



//================== Rectangle.m ==================

@implementation Rectangle

- (void)setWidth:(double)width{
    _width = width;
}

- (void)setHeight:(double)height{
    _height = height;
}

- (double)width{
    return _width;
}

- (double)height{
    return _height;
}


- (double)getArea{
    return _width * _height;
}

@end

正方形

//================== Square.h ==================

@interface Square : Quadrangle
{
    @protected double _sideLength;
}

-(void)setSideLength:(double)sideLength;

-(double)sideLength;

@end



//================== Square.m ==================

@implementation Square

-(void)setSideLength:(double)sideLength{    
    _sideLength = sideLength;
}

-(double)sideLength{
    return _sideLength;
}

- (void)setWidth:(double)width{
    _sideLength = width;
}

- (void)setHeight:(double)height{
    _sideLength = height;
}

- (double)width{
    return _sideLength;
}

- (double)height{
    return _sideLength;
}


- (double)getArea{
    return _sideLength * _sideLength;
}

@end

引用

J_Knight_: 面向对象设计的六大设计原则(附 Demo & UML类图)