OOP Lec14: Design Concept

Design Concept

C++ 运行的载体

在 CPU 上和 GPU 上跑代码是不一样的。

在 Nvidia 的 GPU 上跑程序:CUDA(C-like)

异构 C++:程序员不需要考虑代码到底是在 CPU 上跑还是在 GPU 上跑,由编译器转化成对应指令。

AMD 和 Intel 有各自的异构 C++ 编译器(HCCDPC++),各自收购了一家 FPGA 企业(Xilinx、Ultra)。

Lambda:将一段代码打包成数据,传递给某个计算队列(CPU / GPU / FPGA),根据队列构造出相应指令。

C++ 的场景:嵌入式(Embedded)和 高性能(HPC)

AI 编译:将一段代码(pytorch)理解形成计算图,优化后转化成 GPU 执行的语言,后端 LLVM。

代码设计

设计内容:类的声明、类的函数、类外的函数

问题:研究类的写法,使其有良好的可读性、可维护性、可重用性(understandable, maintainable, reusable)。

所有设计的目标:使未来的更新尽可能简单

软件的开发不是一蹴而就的,强大的软件要经历持续和多层的更新。

可能的更新:性能功能扩展、数据库迁移、移植、适配

现状:杭州的小公司,一个程序员的平均停留时间为 $2$ 年,一个产品平均每 $3$ 年替换一轮所有的开发人员。

责任驱动设计 Responsibility-Driven Design

对于数据而言,处理数据和存放数据的单位应该是同一个。

也即,一个类应该负责控制它存放的所有数据,同时不负责处理任何其他类的数据;

这样在功能更新、出错调试时更容易排查问题、解决问题。

责任驱动设计有助于实现松耦合。

耦合 Coupling

类之间的关联程度。

如果两个类的在实现中紧密联系,那么称为紧耦合。但两个类的联系不可能完全没有,所以我们想要松耦合。

越会被重用的类,它的耦合就应该越松。

效果

对松耦合的类,我们能不阅读其他内容而理解这个类的功能,在尽可能不影响其他类的情况下修改这个类的内容。

解耦合的方法

假设有一个 Button 和一个 Actor,当 Button 被按下时 Actor 跳舞。

在代码层面,Button::onpressed() 时应该运行 Actor::dance()

如果在 Button 内部维护一个 Actor 的指针,同时在 Button::onpressed() 中添加 Actor::dance() 相关内容,这样 Button 编译必须依赖于 Actor,耦合太紧。

注入反转 IoC

常用的方式,尤其是在相对紧密的模块内。

Button 中维护一个 listener指针,通过 addlistener() 修改;

listener 中维护一个 action(),在 Button::onpressed() 时调用;

Actor 继承一下 listener,将 listener::action() 接入 Actor::dance()

class listener{
public:
    virtual void action() = 0;
};
class Button{
public:
    void addlistener(const listener *p){ listenptr = p;}
    void onpressed(){ listenptr->action();}
private:
    listener *listenptr;
};
class Actor: public listener{
public:
    Actor(Button *buttonptr){ buttonptr->addlistener(this);}
    void action(){ dance();}
private:
    void dance();
};

这样 Button 只依赖于 listener,而不同的类继承 listener 后可以方便的实现自己的 action()

现在 Actor 依赖于 Button

消息机制

上一个方案仍然存在依赖关系,尽管依赖关系比较符合逻辑。

在消息机制中,类之间并不认识,但都注册在一个中控系统中。

实现一

每个类有一个唯一的标识(字符串),A 对 B 操作通过向中控发送消息(字符串指令)完成。

这种情况下,中控并不知道信息对应的操作(函数)。

实现二

中控类有许多成员函数,分别指向不同类的成员函数。某个类需要交互的时候,直接调用中控的某个函数;中控的成员函数被调用后,会转接调用对应注册类的函数。功能的替换可以通过继承来实现。

这种情况下,中控需要调用具体的操作(函数)。由于减少了字符串传递和匹配的开销,这种方案的性能更好。

内聚 Cohesion

定义:一个单元(类、函数)负责任务的数量和种类。数量越少、范围越精确,内聚就越高。

效果

高内聚可以方便我们理解类的用途、以及重用类。

策略

一个函数只应该完成一个任务,一个类只应该代表一个东西。

重构 Refactoring

问题

代码在维护时,通常代码会越来越长。

如果一直下去,代码往往会因为零碎的更新而降低了内聚、提升了耦合。

重构的收益是延时的,对当下影响可能不大,但对未来的开发有益。

所以我们需要适时重构代码。

功能越单一的代码,修改的可能性也更小。

回归测试

在使用重构的代码前,先要用重构前的代码测试其功能是否一致。

可维护性 与 可扩展性:可扩展性指不经修改就能增加新的功能;可维护性指经少量修改就能增加新的功能。

总结

  • 项目内容是持续更新的。
  • 我们需要让更新尽可能简单。
  • 好的代码应该有高可读性、可维护性、可重用性
  • 代码的质量不仅仅要求把功能实现,更要求松耦合、高内聚,避免代码重复。
  • 代码的质量将直接影响更新内容时的工作量。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇