如何解决解释 Robin C. Martin 关于过程式编程 vs OOP 的“干净代码”
Robin C. Martin 的“干净的代码”有一个关于过程编程与面向对象编程的部分,其中提出了一些我无法理解的陈述。 有人愿意更详细地解释这些陈述背后的思考过程吗?
声明如下:
过程代码(使用数据结构的代码)使得在不改变现有数据结构的情况下轻松添加新函数。另一方面,OO 代码可以轻松添加新类,而无需更改现有功能。
补语也是对的:
程序代码很难添加新的数据结构,因为所有的功能都必须改变。 OO 代码很难添加新函数,因为所有类都必须更改。
用于证明此语句的代码示例如下。
程序代码示例:
public class Square {
public Point topLeft;
public double side;
}
public class Rectangle {
public Point topLeft;
public double height;
public double width;
}
public class Circle {
public Point center;
public double radius;
}
public class Geometry {
public final double PI = 3.14;
public double area(Object shape) throws NoSuchShapeException {
if (shape instanceof Square) {
Square s = (Square) shape;
return s.side * s.side;
}
else if (shape instanceof Rectangle) {
Rectangle r = (Rectangle) shape;
return s.height * s.width;
}
else if (shape instanceof Circle) {
Circle c = (Circle) shape;
return PI * c.radius * c.radius;
}
throw new NoSuchShapeException();
}
}
现在,让我们在这里暂停一下。让我们假设 Geometry 不仅计算面积,还计算周长。按照上面的例子,很明显:
因此,Procedural 使添加新函数变得容易,但添加新数据结构变得困难。
但是让我们考虑一下上面的例子,它是用所有典型的过程语言 C 编写的。 使用 C,我们无法编写一个函数,例如采用抽象数据类型的 Geometry's area。相反,我们将使用以下方法签名:
int geometry_area(square s)
int geometry_area(rectangle r)
int geometry_area(circle c)
int geometry_perimeter(square s)
int geometry_perimeter(rectangle r)
int geometry_perimeter(circle c)
考虑到这一点,添加一个新的形状并不会强制改变任何功能,这与作者所说的相矛盾。取而代之的是,我们必须在其独立函数中为新形状实现所有行为。
所以,总的来说,我对作者推理背后的逻辑有点困惑。
作者总结如下:
在任何复杂的系统中,有时我们想要添加新的数据类型而不是新的函数。对于这些被反对的情况,OO 是最合适的。另一方面,有时我们会想要添加新函数而不是数据类型。在这种情况下,程序代码和数据结构会更合适。
解决方法
您正在谈论第 6 章 - 对象和数据结构,小节数据/对象反对称。
我认为书中的方法故意使用面向对象的语言来展示他所说的“反对称”。您正在尝试使用过程语言 C 来遵循他的论点。
你可以用 C 重构书中的例子。
#include <stdlib.h>
#include <math.h>
#include <stdio.h>
#include <memory.h>
#define M_PI 3.14159265358979323846
typedef enum {
RECTANGLE = 0x0,CIRCLE = 0x1
} geometry_type_t;
typedef struct {
geometry_type_t type;
} geometry_t;
//radius in millimeters to avoid floats
typedef struct {
int radius;
} circle_t;
//width and height in millimeters to avoid floats
typedef struct {
int width;
int height;
} rectangle_t;
int geometry_area(geometry_t* geometry) {
if (geometry->type == RECTANGLE) {
rectangle_t* rect = (rectangle_t*)(geometry + 1);
return (rect->height * rect->width);
}
else if (geometry->type == CIRCLE) {
circle_t* circle = (circle_t*)(geometry + 1);
//I don't care about floatings in this example
return (circle->radius * circle->radius * M_PI);
}
}
int main(int argc,char* argv[]) {
circle_t c;
c.radius = 2000; //millimeters
geometry_t* geom = malloc(sizeof(geometry_t) + sizeof(circle_t));
if (geom == NULL) {
return EXIT_FAILURE;
}
geom->type = CIRCLE;
memcpy((geom + 1),&c,sizeof(circle_t));
printf("Area of circle with radius %d: %d\n",c.radius,geometry_area(geom));
rectangle_t r;
r.height = 1000;
r.width = 2000;
geom->type = RECTANGLE;
memcpy((geom + 1),&r,sizeof(rectangle_t));
printf("Area of rectangle with width %d and height %d: %d",r.width,r.height,geometry_area(geom));
}
如果您遵循您提到的方法,为书中的每个 shape
短语提供单独的函数:
... if I add a new shape,I must change all the functions in Geometry to deal with it
可以改写为:
adding a new shape forces you to reimplement any associated function for that specific shape
还有声明:
... if a perimeter() function were added to Geometry. The shape classes would be unaffected
可以改写为:
adding a new function for multiple structs needs to be done individually for each struct
在我看来作者的解释只有在使用某种抽象时才有意义。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。