gpt4 book ai didi

language-agnostic - HTML解析类的构造函数应该做多少工作?

转载 作者:行者123 更新时间:2023-12-03 08:34:41 25 4
gpt4 key购买 nike

对象构造函数可以完成多少工作?它应该只初始化字段而不对数据实际执行任何操作,还是可以进行一些分析?

背景:
我正在编写一个类,负责解析HTML页面并根据已解析的信息返回各种信息。该类的设计使得该类的构造函数进行解析,如果发生错误,则引发异常。实例一旦初始化,就可以使用解析的值,而无需通过访问器进行进一步处理。就像是:

public class Parser {

public Parser(final String html) throws ParsingException {
/* Parsing logic that sets private fields */
/* that throws an error if something is erroneous.*/
}

public int getNumOfWhatevers() { return private field; }
public String getOtherValue() { return other private field; }
}

设计完类(class)后,我开始怀疑这是否是正确的面向对象实践。解析代码是否应该放在 void parseHtml()方法中,并且访问器仅在调用此方法后才返回有效值?我觉得我的实现似乎是正确的,但是我不禁感到一些面向对象的纯粹主义者可能会由于某些原因而认为它是不正确的,并且如下所示的实现会更好:
public class Parser {

public Parser(final String html) {
/* Remember html for later parsing. */
}

public void parseHtml() throws ParsingException {
/* Parsing logic that sets private fields */
/* that throws an error if something is erroneous.*/
}

public int getNumOfWhatevers() { return private field; }
public String getOtherValue() { return other private field; }
}

是否存在实例中不应在构造函数中发生初始化代码(例如,解析信息)的情况,还是我只是在愚蠢地猜测自己?

从构造函数拆分解析有什么好处/缺点?

有什么想法吗?有见识?

最佳答案

我通常遵循一个简单的原则:

对于类实例的正确存在和行为必不可少的所有事物都应传递并完成到构造函数中。

其他所有 Activity 均通过其他方法完成。

构造函数永远不能:

  • 使用类的其他方法以使用覆盖行为
  • 通过
  • 方法对其私有(private)属性进行操作

    因为我了解了很难的方法,所以当您处于构造函数中时,对象处于不连贯的中间状态,这太危险了,无法处理。某些意外行为可能是您的代码所预期的,某些可能是由于语言体系结构和编译器的决定而引起的。永远不要猜测,保持安全,尽量少。

    在您的情况下,我将使用Parser::parseHtml(file)方法。解析器的实例化和解析是两个不同的操作。当您实例化解析器时,构造函数会将其置于条件中以执行其工作(解析)。然后,使用其方法执行解析。然后,您有两个选择:
  • 您可以允许解析器包含解析结果,并为客户端提供一个接口(interface)以检索解析的信息(例如Parser::getFooValue())。如果您尚未执行解析,或者解析失败,则方法将返回Null。
  • 或您的Parser::parseHtml()返回一个ParsingResult实例,其中包含解析器找到的内容。

  • 第二种策略为您提供更好的粒度,因为解析器现在是无状态的,并且客户端需要与ParsingResult接口(interface)的方法进行交互。解析器界面保持简洁流畅。 Parser类的内部结构倾向于遵循 Builder pattern

    您评论说:“我感觉好像返回了一个没有解析任何内容的解析器实例(正如您所建议的),一个构造函数已经失去了它的目的。如果没有实际解析信息的意图,初始化解析器是没有用的。所以如果肯定要进行解析,我们是否应该尽早进行解析,并尽早报告并报告错误,例如在构建解析器的过程中?我觉得初始化具有无效数据的解析器应该导致抛出错误。”

    并不是的。如果返回解析器的实例,那么它将进行解析。在Qt中,实例化一个按钮时,当然会显示它。但是,您可以使用QWidget::show()方法在用户看不到之前手动调用。

    OOP中的任何对象都涉及两个问题:初始化和操作(忽略终结处理,现在不在讨论中)。如果将这两个操作放在一起,则既会带来麻烦(对象操作不完整),又会失去灵活性。有很多原因导致您在调用parseHtml()之前执行对象的中间设置。示例:假设您想将解析器配置为严格(如果表中的给定列包含字符串而不是整数,则失败)或允许。或者注册每次执行或结束新解析时都会发出警告的侦听器对象(请考虑GUI进度栏)。这些是可选的信息,如果您的体系结构将构造函数用作可以完成所有工作的übermethod,则最终会产生大量可选方法参数和条件列表,这些参数和条件可以处理本质上属于雷区的方法。

    “缓存不应由解析器负责。如果要缓存数据,则应创建单独的缓存类以提供该功能。”

    相反。如果您知道要对许多文件使用解析功能,并且以后很有可能再次访问和解析这些文件,则解析器的内部职责是对哪些内容执行智能缓存。它已经看到了。从客户端的 Angular 来看,是否执行此缓存是完全可以忽略的。他仍在调用解析,并仍在获得结果对象。但它的答案要快得多。我认为没有比这更好的证明分离关注点了。契约(Contract)界面或整个软件体系结构完全不变,您可以提高性能。

    但是,请注意,我并不主张您绝对不应使用构造函数调用来执行解析。我只是声称这有潜在危险,并且您失去了灵活性。有很多示例,其中构造函数位于对象实际 Activity 的中心,但也有很多相反的示例。示例(尽管有偏见,它源于C风格):在python中,我认为这样的事情很奇怪
    f = file()
    f.setReadOnly()
    f.open(filename)

    而不是实际的
    f = file(filename,"r")

    但是我确信有使用第一种方法的IO访问库(第二种是糖语法方法)。

    编辑:最后,请记住,尽管将来添加构造函数“快捷方式”很容易且兼容,但是如果发现此功能危险或有问题,则无法删除此功能。显而易见,添加接口(interface)要比删除操作容易得多。含糖的行为必须权衡您将来必须为该行为提供的支持。

    关于language-agnostic - HTML解析类的构造函数应该做多少工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1183531/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com