【问题标题】:Understanding abstraction in OOP理解 OOP 中的抽象
【发布时间】:2019-12-11 17:53:36
【问题描述】:

我正在研究面向对象的概念,抽象概念基本上被描述为对用户隐藏实现。因此,如果一个类中有一个成员函数,并且我们为某些任务调用该函数,抽象表示用户不应该关心事情是如何完成的,而应该只知道正在完成什么。但即使在非面向对象的编程风格中,如果我们编写一个函数,整个任务也只需调用一个函数即可完成。它不也遵循抽象逻辑吗?或者,OOP 中的抽象和函数式编程有什么区别?

【问题讨论】:

  • 抽象是一个非常广泛的话题,但对于 oop,我会学习多态性。相同的概念在 FP 中以某种方式起作用,您的“接口”就是类型类或函数签名。
  • 是的,抽象是为了让用户远离细节。您也可以在函数式编程中实现这一点。事实上,很多时候你可能不知道也不关心特定函数是如何派生的,例如,可能是由不同的其他函数组成的。因此,抽象在这两种情况下都成立——它是为了让细节远离调用代码。不同之处在于抽象与其他技术结合使用时 - 正如@AlexHart 所说,它在多态中发挥了很大作用。
  • 对用户隐藏实现”我称之为封装information hiding。是的,函数/过程也可以做到这一点,你不一定需要对象——这个概念对 OOP 很重要的是对象是封装的单位。
  • 是的,一个函数很自然地从一个表达式中抽象出来,它也概括了它。但它在没有状态的情况下完成这一切,并且除了将输入映射到输出之外没有其他影响。没有与数据的绑定,而只是对数据具有什么类型的期望。如果您正在寻找两种范式之间的区别,您应该研究相反的概念:具体化。 FP基本上是用值编程。函数就是值。甚至效果也由值表示并封装在值中。我认为 OOP 中没有对应物——不过我不是专家。

标签: oop functional-programming abstraction


【解决方案1】:

在面向对象编程中,我们通常从继承和多态的角度来考虑抽象。

让我们考虑Writer 接口

interface Writer {
    void write(byte[] bytes)
}

这个接口允许用户写……东西。什么,我们并不特别担心。我们可以有多个版本:

class FileWriter implements Writer

class StringWriter implements Writer

class LogWriter implements Writer

class MySuperCustomWriter implements Writer

我们在哪里写并不重要,可以是FileString、套接字或任何地方。我们要做的就是写一些东西。这让我们可以编写如下代码:

public class MyBusinessLogic {

    private final Writer writer;

    public MyBusinessLogic(Writer writer) {
        this.writer = writer;
    }

    void someBusinessLogic() {
        // .. 
        writer.write(someStuff);
    }
}

我们这里有一些想要编写的业务逻辑。通过使用接口,我们的业务逻辑不再依赖于任何特定的编写方法。它只是得到一些能够进行写作的对象。我们可以将任何示例编写器传递给它,并确保它可以正常工作,因为我们在这里对编写行为感兴趣,而不是实现。

通过这样做,业务逻辑不依赖于文件系统、网络或其他任何东西。

【讨论】:

    【解决方案2】:

    这不是您调用提供抽象的函数的事实,而是调用它的庄园。例如,你可能有一个函数可以让你写一行文本:

    void writeLine(string fileName, string value)
    

    但这并不是从您正在写入文件这一事实中抽象出来的。抽象版本不需要调用者提供fileName 参数,因为它特定于函数的特定实现。相反,您会:

    void writeLine(string value)
    

    并且文件名是使用另一种机制提供的,例如如果您使用 OOP 并调用 writeLine 方法,则为类的构造函数参数,或者在函数式情况下,您可能会 curry 原始函数以创建抽象版本。

    【讨论】:

    • "但这并不是从您正在写入文件这一事实中抽象出来的。" 但是用户是从实际实现中抽象出来的。例如,数据是否被分块成 512 字节的块来写入或其他。 就是抽象的意义——实现细节。您是否写入文件也可以“隐藏” - 例如,通过具有接口Writer,然后使用不同的实现FileWriterConsoleWriter,但这不是抽象的核心所在。用户不知道也不关心行的精确书写方式,这才是最重要的。
    • 如果函数需要你传入要写入的字符串、文件名、块大小等,调用函数的行为并不提供抽象。调用者需要知道所有这些细节对实现很重要,这意味着它根本不是从实现中抽象出来的。重要的部分是不要求调用者需要提供比他们已经拥有的信息更多的东西。不需要调用者为了能够调用函数而进行更改。
    • 这是正确的,提供文件名是完全可行的。例如,用户在与问题交互时点击“保存”并提示输入文件名 - 在 那种 的情况下,调用该方法来写入信息的代码将知道文件名是什么。与记录器相比——调用代码通常不知道输出的去向——它只会调用log(someData),然后将输出到文件、控制台、网络位置,可能不止一次一个。因此,采用文件名的方法本身并不缺乏抽象。
    【解决方案3】:

    快速封装示例

    type
        public class DateTimeClass
           private 
              Era: integer;
              Culture: integer;
              Year: integer;
              Month: integer;
              Day: integer;
    
          protected
               Integer function getYear ( );
    
               // other functions
    
               procedure setYear ( );
    
              // other functions
          public
              procedure AssignOccidentalDate
                   (NewYear: integer; NewMonth: integer;
                        NewDay : integer);
       end;
    
       ...
    
       var Date: DateTimeClass;
    
      Date.AssignOccidentalDate (2019, 07, 27);
      ...
    

    您只能访问“公共”声明。

    【讨论】:

      猜你喜欢
      • 2014-07-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-24
      • 2016-11-28
      • 1970-01-01
      • 2013-05-30
      • 1970-01-01
      相关资源
      最近更新 更多