这是一个简单的例子,但它反映了坚实的原则之间的紧张,我经常发现自己在挣扎。
开放/封闭原则的一个流行示例(例如,[1]、[2])设想您有许多形状类,以及一个drawShape()方法,例如:
// Open-Close Principle - Bad example
class GraphicEditor {
public void drawShape(Shape s) {
if (s.m_type==1)
drawRectangle(s);
else if (s.m_type==2)
drawCircle(s);
}
public void drawCircle(Circle r) {....}
public void drawRectangle(Rectangle r) {....}
}使用开放/封闭原则(OCP),我们观察到,对于添加的每一个新形状,都需要修改该代码;我们希望关闭修改的需要,并允许扩展作为添加新行为的更好方式。
然后,这些示例提供了以下与OCP兼容的解决方案:
// Open-Close Principle - Good example
class GraphicEditor {
public void drawShape(Shape s) {
s.draw();
}
}
class Shape {
abstract void draw();
}
class Rectangle extends Shape {
public void draw() {
// draw the rectangle
}
} 作为OCP的抽象演示,这是一个很好的例子。然而,在我看来,这个解决方案公然违反了单一责任原则(SRP)。我们从将“绘图责任”分离为自己的类开始,并通过将“绘图责任”与形状的简单定义结合起来来解决OCP问题。
例如,如果绘图涉及到与GUI的交互,则此示例将简单形状的定义与GUI操作结合在一起!这似乎不对。
发布于 2017-03-20 12:04:10
画成画布。绘图不应要求与UI进行交互,只需将绘制的形状转移到UI以供其显示。
interface Shape {
void draw(Canvas canvas);
}
class GraphicEditor {
private Canvas mainCanvas;
void drawShape(Shape s, Point location) {
Canvas subCanvas = mainCanvas.subCanvas(location, s.width, s.height);
s.draw(subCanvas);
mainCanvas.copy(subCanvas, location);
}
}一个更笼统的答案是:
设计传输数据的方法,而不是产生副作用。定义DTO(像画布一样)并将它们传递到层次结构中。
每个物体都有合作者。将它们视为父对象用于获取信息和执行不属于其核心责任(SRP)的任务的服务。由此产生的DTO是这些服务的“产品”。父对象可以随心所欲地处理这些产品(在屏幕上绘制它们,通过线发送它们,将它们存储在DB中,.)
发布于 2020-04-16 08:13:19
还有一种思考这个问题的方法。
考虑正在重构的代码:
// Original code
class GraphicEditor {
public void drawShape(Shape s) {
if (s.m_type==1)
drawRectangle(s);
else if (s.m_type==2)
drawCircle(s);
}
public void drawCircle(Circle r) {....}
public void drawRectangle(Rectangle r) {....}
}似乎原来的代码已经“涉及到与GUI的交互”。类Shape、Circle和Rectangle已经是原始代码的一部分。因此,在我看来,这些类也“与GUI交互”。
如果我们将名称更改为GraphicalShape、GraphicalCircle和GraphicalRectangle,则可能会消除混淆。
另一种说法是:
重构所做的(符合OCP)是将代码(其中“涉及到与GUI的交互”)从drawCircle(Circle c).
public void drawCircle(Circle c) {
// draw the circle, c
// might have "involved interaction with a GUI"
}...into draw()方法在Circle类中
class GraphicalCircle extends GraphicalShape {
public void draw() {
// draw the circle, this
// might "involve interaction with a GUI"
}
} 你应该记住,这样做是有目的的。(你这样做并不仅仅是为了遵守坚实的原则。)目标是,如果需要添加另一个形状,则不需要修改GraphicalEditor类。您的GraphicalEditor类由于修改(与绘制新形状有关)而关闭,但对扩展(绘制新形状)开放。
(如果您首先知道只绘制圆圈和矩形,则不需要进行重构以符合OCP。)
通过遵守OCP,您改变了GraphicalEditor的责任。以前,它有两项职责:(1)编排形状的绘制(2)绘制形状。
现在,它只有一个责任:精心设计形状的绘制。您移除了将圆圈从GraphicalEditor绘制到GraphicalCircle类的责任。以及将矩形绘制到GraphicalRectangle类中的责任。
另一种说法是:
Circle类的目的是包含GUI对圆的责任。因此,即使它包含与GUI交互的代码,它也遵守SRP。
Circle.draw()类的目的是包含绘制圆图形用户界面的职责。因此,即使它包含与GUI交互的代码,它也遵守SRP。
https://softwareengineering.stackexchange.com/questions/344529
复制相似问题