[供给侧]一个开源项目(xserver)18年前的一次重构 (转载)

darkk
darkk体国经野 义尚光大 08月29日 字数 3535

(1) 这个项目的简要信息(参考)

=================================

维毒百科:

** Graphical User Interface **

The graphical user interface (GUI jee-you-eye or) is a form of user interface that allows users to interact with electronic devices through graphical icons and audio indicator such as primary notation, instead of text-based user interfaces, typed command labels or text navigation. GUIs were introduced in reaction to the perceived steep learning curve of command-line interfaces (CLIs), which require commands to be typed on a computer keyboard.

** X.Org Server **

X.Org Server is the free and open-source implementation of the X Window System display server stewarded by the X.Org Foundation. Implementations of the client side of the protocol are available e.g. in the form of Xlib and XCB.

简单来说, xserver 是开源业界广泛使用的一套图形用户界面解决方案,特别的,是一种广泛使用、历史悠久的基础设施级别的一个跨硬件、跨硬件体系架构、跨操作系统平台、面向不同的图形化用户应用程序的整体解决方式。

(2) 这次重构的相关代码

=========================

这里只讨论一点,即 mi/miinitext.c 中的 InitExtensions 函数的实现。

这次重构并没有改变 InitExtensions 的在C语言中的“声明”,仍然是:

void

InitExtensions(argc, argv)

int        argc;

char    *argv[];

但是他们具体的实现方式改变了。在重构之前:

https://gitlab.freedesktop.org/xorg/xserver/-/blob/9508a382f8a9f241dab097d921b6d290c1c3a776/mi/miinitext.c#L153

在重构之后:

https://gitlab.freedesktop.org/xorg/xserver/-/blob/d568221710959cf7d783e6ff0fb80fb43a231124/mi/miinitext.c#L507

具体是这样子的:

① 在重构之前, void InitExtensions(argc, argv) 的函数本体主要大概是这个样子的:

```

InitExtensions(argc, argv) {

#ifdef Extension_A_MACRO

Extension_A_Init();

#endif

#ifdef Extension_B_MACRO

Extension_B_Init();

#endif

...

}

```

这是“指令序列”(A series of Order)

② 重构之后:

重构引入了一个静态化的结构体数组 staticExtensions :

```

static ExtensionModule staticExtensions[] = {

#ifdef Extension_A_MACRO

{ Extension_A_Init_FUNPTR, "Extension_A_Named",  Extension_A_settings... },

#endif

#ifdef Extension_B_MACRO

{ Extension_B_Init_FUNPTR, "Extension_B_Named",  Extension_A_settings... },

#endif

...

}

```

并且通过迭代这个数组来具体调用 Extension_α_Init 了:

```

void

InitExtensions(argc, argv)

int        argc;

char    *argv[];

{

int i;

ExtensionModule *ext;

static Bool listInitialised = FALSE;

if (!listInitialised) {

/* Add built-in extensions to the list. */

for (i = 0; staticExtensions[i].name; i++)

LoadExtension(&staticExtensions[i], TRUE);

/* Sort the extensions according the init dependencies. */

LoaderSortExtensions();

listInitialised = TRUE;

}

for (i = 0; ExtensionModuleList[i].name != NULL; i++) {

ext = &ExtensionModuleList[i];

if (ext->initFunc != NULL &&

(ext->disablePtr == NULL ||

(ext->disablePtr != NULL && !*ext->disablePtr))) {

(ext->initFunc)();

}

}

}

```

是不是有点“多态”、“封装”的感觉?

这种实现方式实际上是通过定义一个声明式的链表,然后通过将这个链表作为数据结构,然后建立算法。

(3) 一些结论

==============

-- 这次重构将指令序列改成了声明式链表及相关函数

-- 这次重构之后代码更长了,但可读性提高了,而且函数中的“过程”被转化为了“数据结构”

我认为这个重构非常切合 xserver 这样子的软件,极大地提高了其可维护性。

堪称一次史诗级的重构!

Programming 编程技术
27 个回复
hgoldfish
老鱼 08月30日

弱问。重写一个 xserver 难吗?

【 在 darkk (darkk体国经野 义尚光大) 的大作中提到: 】

: 【 以下文字转载自 SoftEng 讨论区 】

: 发信人: darkk (darkk体国经野 义尚光大), 信区: SoftEng

: 标  题: [供给侧]一个开源项目(xserver)18年前的一次重构

: ...................

xiaoju
可爱的龙猫 08月30日

Xorg

【 在 hgoldfish (老鱼) 的大作中提到: 】

: 弱问。重写一个 xserver 难吗?

DoorWay
DoorWay 08月30日
DoorWay
DoorWay 08月30日

函数中的“过程”被转化为了“数据结构”—— 这个很好。我一直主张这样,表达业务意义。但是cpp老鸟则认为,这样太面向对象,so boring, so java。不够cpp。

ArchLinux
a lightweight and flexible distribution 08月30日

这段代码是C代码而不是C++代码。

【 在 DoorWay (DoorWay) 的大作中提到: 】

: 函数中的“过程”被转化为了“数据结构”—— 这个很好。我一直主张这样,表达业务意义。但是cpp老鸟则认为,这样太面向对象,so boring, so java。不够cpp。

DoorWay
DoorWay 08月30日

你说的对。

我也在尽量避免混淆,引用了其中一句,“将过程转化为数据结构”

a series of order —> data structure  (and the algorithm that operates them)

data structure and its behavior ,就是我们熟悉的类的定义,也是面向对象的初衷。

我一直主张少写 series of order,多写 data structure,类名就是业务实体(概念),函数名就是业务行为(概念),加在一起就是业务模型。

如果业务对象特别复杂,可以将其单纯的定义为数据对象。再配套一个或多个handler/operator。 就是visitor模式的变形。结构和行为分离。结构稳定、行为复杂。

网上的一些cpp老鸟,认为这样太啰嗦,太java,倾向于一个函数1000行,解决问题。另一些倾向于这种做法的群体,据我观察,是下凡的科学家,自诩“我在写算法”,“这个算法模型就是有这么多变量”。我认为要么是傲慢,要么是无知。大抵是不屑于与engineer为伍的,何况code engineer,码农。

【 在 ArchLinux 的大作中提到: 】

: 这段代码是C代码而不是C++代码。

xieyf
绿蚁新醅酒,红泥小火炉 08月30日

无语,一把重构了了事,这点破事还整出一次“史诗级”重构

【 在 darkk 的大作中提到: 】

: 【 以下文字转载自 SoftEng 讨论区 】

: 发信人: darkk (darkk体国经野 义尚光大), 信区: SoftEng

: 标 题: [供给侧]一个开源项目(xserver)18年前的一次重构

: 发信站: 水木社区 (Sun Aug 29 21:37:18 2021), 站内

lvsoft
Lv(The Last Guardian) 08月30日

这....

基本上所有linux下c代码都是这个风格...

举个例子:

static struct fuse_oprations hello_oper = {

.getattr = hello_getattr,

.readdir = hello_readdir,

.open    = hello_open,

.read    = hello_read,

};

这就是C的OO实现方式,稀松平常了

【 在 DoorWay 的大作中提到: 】

: 你说的对。

: 我也在尽量避免混淆,引用了其中一句,“将过程转化为数据结构”

: a series of order —> data structure  (and the algorithm that operates them)

: ...................

DoorWay
DoorWay 08月30日

对,这样的风格很好。还有强转指针,模拟downcast的。让人可以想明白原来继承是要解决这样的问题。

语言的机制有没有是一回事,编程背后的想法(概念、逻辑、思想、哲学)是另外一回事。脑子清楚的人,写得代码不会太差。

这个重构就是做了这样的事,很小的事。但转帖的描述,“a series of order” Vs 数据结构,让我觉得道不孤,高兴之余,说两句。

另外我发现,用a series of order 的思路,来思考多线程的模型,或者co-routine,是非常好的。一个线程就是一组有序的指令,这些指令重排列,加减一点新指令,就是另一个线程。切换线程就是切换序列,线程context就是对序列的执行位置、状态的记录,对象的析构函数也是一组指令,append到序列A还是序列B,都可以(不得访问原构造序列的局部数据)。 用面向对象的思路来思考co-routine,就很阻碍。

【 在 lvsoft 的大作中提到: 】

: 这....

: 基本上所有linux下c代码都是这个风格...

: 举个例子:

: ...................

beijing2duck
beijing2duck 08月30日

Very kind of classic OCP usage.

darkk
darkk体国经野 义尚光大 08月31日

小朋友,我劝你谦虚一点

【 在 xieyf 的大作中提到: 】

: 无语,一把重构了了事,这点破事还整出一次“史诗级”重构

darkk
darkk体国经野 义尚光大 08月31日

其实不能一概而论的。。

这里涉及到 xserver 的实现思路,总得来说他们好像是不断地扩充了“插件”的内涵,直到当年已经有一大堆了。。然后估计他们意识到性质变了,已经不是 hello windows, hello mac , hello linux 那种事了。。

a series of order 也有优点的,尤其是一些完整性是内聚的不能“扩展”的场合,保持 a series of order 甚至是正确性和算法可以结束的基础。(一时半会我也说不出来)

【 在 DoorWay 的大作中提到: 】

: 你说的对。

: 我也在尽量避免混淆,引用了其中一句,“将过程转化为数据结构”

: a series of order —> data structure  (and the algorithm that operates them)

: ...................

darkk
darkk体国经野 义尚光大 08月31日

这里是史诗级的重构的原因在于,这次重构标志着他们对 xserver 这样子一个项目的理解接近承认,其内部的功能已经被认为是清晰的(不然无法改成数据结构)

这跟那种一上来先搞一套“数据结构+算法”没有太大关系。主要的意义在于面向过程和面向对象的关系如何被把握,这个被把握的问题才是“史诗级”的前提。

简单来说,这里体现了一种深邃的辩证法,作为结果的代码的变革的背后的思想的变化才是我评价的关键,不是设计模式的“应用”的问题,而是“设计模式”的原理的问题。

darkk
darkk体国经野 义尚光大 08月31日
2508618 (1012.7 K)

反正我写不出来。。

源码有个文档的,感觉可以看一下……

由于需求已经梳理了,肯定比他们当年从0开始简单。。但是没有机器学习啥的,主要是熟悉各种硬件软件的接口,然后健壮性和兼容性怎么能搞好……

反正我现在做不出来……

跟设计模式没啥关系,就算知道全部设计模式了,也就是有了个框架,其他的不懂还是一行都写不了……

【 在 hgoldfish 的大作中提到: 】

: 弱问。重写一个 xserver 难吗?

alextooter
来了 08月31日

十几年前C项目里面也是这样写的。

这个很稀奇吗?当年一堆书介绍过这种写法,什么c oop, c interface之类的

DoorWay
DoorWay 08月31日

wayland算xserver对标物吗?我看从发起到现在的成熟度,是非常难的。

当然可能是没有金主没有动力。

当年chrome说横空出世就出世了。 火狐就稀里哗啦了…… 刚查了下,7.69%

【 在 darkk 的大作中提到: 】

: 反正我写不出来。。

: 源码有个文档的,感觉可以看一下……

: 由于需求已经梳理了,肯定比他们当年从0开始简单。。但是没有机器学习啥的,主要是熟悉各种硬件软件的接口,然后健壮性和兼容性怎么能搞好……

: ...................

DoorWay
DoorWay 08月31日

你这个史诗级,是红军二万五千里的史诗,还是敏捷开发里的epic?

这个被把握的问题,是“Plugin”即插即用的架构设计吗?

没懂你为何这么拔高。

【 在 darkk 的大作中提到: 】

: 这里是史诗级的重构的原因在于,这次重构标志着他们对 xserver 这样子一个项目的理解接近承认,其内部的功能已经被认为是清晰的(不然无法改成数据结构)

: 这跟那种一上来先搞一套“数据结构+算法”没有太大关系。主要的意义在于面向过程和面向对象的关系如何被把握,这个被把握的问题才是“史诗级”的前提。

: 简单来说,这里体现了一种深邃的辩证法,作为结果的代码的变革的背后的思想的变化才是我评价的关键,不是设计模式的“应用”的问题,而是“设计模式”的原理的问题。

tgfbeta
右旋肉碱 09月01日

"格林斯潘第十定律"(Greenspun's Tenth Rule):

"任何C或Fortran程序复杂到一定程度之后,都会包含一个临时开发的、只有一半功能的、不完全符合规格的、到处都是bug的、运行速度很慢的Common Lisp实现。"

【 在 darkk (darkk体国经野 义尚光大) 的大作中提到: 】

: 【 以下文字转载自 SoftEng 讨论区 】

: 发信人: darkk (darkk体国经野 义尚光大), 信区: SoftEng

: 标  题: [供给侧]一个开源项目(xserver)18年前的一次重构

: ...................

darkk
darkk体国经野 义尚光大 09月01日

这就是十八年前的代码啊 = = 。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

【 在 alextooter 的大作中提到: 】

: 十几年前C项目里面也是这样写的。

: 这个很稀奇吗?当年一堆书介绍过这种写法,什么c oop, c interface之类的