首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >我如何使这个开放/封闭的例子也遵守单一责任?

我如何使这个开放/封闭的例子也遵守单一责任?
EN

Software Engineering用户
提问于 2017-03-20 11:57:59
回答 2查看 933关注 0票数 7

这是一个简单的例子,但它反映了坚实的原则之间的紧张,我经常发现自己在挣扎。

开放/封闭原则的一个流行示例(例如,[1][2])设想您有许多形状类,以及一个drawShape()方法,例如:

代码语言:javascript
复制
// 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兼容的解决方案:

代码语言:javascript
复制
// 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操作结合在一起!这似乎不对。

,什么是符合OCP和SRP的好的重新设计?

EN

回答 2

Software Engineering用户

回答已采纳

发布于 2017-03-20 12:04:10

画成画布。绘图不应要求与UI进行交互,只需将绘制的形状转移到UI以供其显示。

代码语言:javascript
复制
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中,.)

票数 14
EN

Software Engineering用户

发布于 2020-04-16 08:13:19

还有一种思考这个问题的方法。

考虑正在重构的代码:

代码语言:javascript
复制
 // 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的交互”。类ShapeCircleRectangle已经是原始代码的一部分。因此,在我看来,这些类也“与GUI交互”。

如果我们将名称更改为GraphicalShapeGraphicalCircleGraphicalRectangle,则可能会消除混淆。

另一种说法是:

重构所做的(符合OCP)是将代码(其中“涉及到与GUI的交互”)从drawCircle(Circle c).

代码语言:javascript
复制
public void drawCircle(Circle c) {
    // draw the circle, c
    // might have "involved interaction with a GUI"
}

...into draw()方法在Circle类中

代码语言:javascript
复制
 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。

票数 0
EN
页面原文内容由Software Engineering提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://softwareengineering.stackexchange.com/questions/344529

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档