理解面向对象编程中的抽象化

3

我正在学习面向对象的概念,其中抽象概念基本上被描述为将实现隐藏在用户之外。因此,如果类中有一个成员函数,并且我们调用该函数执行某些任务,抽象化意味着用户不应关心如何完成任务,而只应知道正在完成什么。但即使在非面向对象编程风格中,如果我们编写一个函数,整个任务也可以通过简单地调用一个函数来完成。这是否也遵循了抽象逻辑?或者说,在面向对象编程和函数式编程中的抽象有什么区别吗?


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

1

提供抽象的不是函数的调用本身,而是调用方式。例如,您可能有一个允许您编写文本行的函数:

void writeLine(string fileName, string value)

但这并不是抽象出来的事实,因为你正在写入文件。抽象化的版本不需要调用者提供fileName参数,因为它是特定于该函数实现的。相反,你将拥有:

void writeLine(string value)

如果您正在使用面向对象编程并调用writeLine方法,或者在函数式情况下,可以对原始函数进行柯里化以创建抽象版本,则可以使用另一种机制(例如类的构造函数参数)提供fileName。


1
“但这并不意味着你正在写入文件。” 用户与实际实现是分离的。例如,数据是否被分块为512字节块进行写入或其他方式。这就是抽象化的含义 - 实现细节。无论您是否写入文件也可以被“隐藏” - 例如,通过具有接口Writer和不同实现FileWriterConsoleWriter,但这不是抽象化的核心所在。用户不知道也不关心行的精确写入方式,这才是重要的。 - VLAZ
调用函数的行为并不提供抽象,如果函数需要您传递要写入的字符串、文件名、块大小等。调用者需要知道所有这些对实现很重要的细节,这意味着它根本没有从实现中抽象出来。重要的是不要要求调用者提供除他们已经拥有的信息之外的任何东西。不要要求调用者改变以便能够调用该函数。 - dezfowler
这是正确的,而且提供文件名是完全可行的。例如,当用户与问题交互时,点击“保存”并提示输入文件名 - 在*那种情况下,调用写入信息方法的代码将知道文件名是什么。相比之下,例如记录器 - 调用代码通常不知道也不关心输出去哪里 - 它只会调用log(someData),然后将其输出到文件、控制台、网络位置,甚至可能同时输出到多个位置。因此,接受文件名的方法本质上并不缺乏抽象化。 - VLAZ

1
在面向对象编程中,我们通常从继承和多态的角度来思考抽象概念。
让我们考虑一下“Writer”接口。
interface Writer {
    void write(byte[] bytes)
}

这个接口允许用户写入...某些内容。具体是什么我们不太担心。我们可以有多个版本:

class FileWriter implements Writer

class StringWriter implements Writer

class LogWriter implements Writer

class MySuperCustomWriter implements Writer

我们写代码的地方并不重要,可以是文件字符串、套接字或其他任何地方。我们只想往某个地方写入内容。这样,我们就可以编写如下代码:

public class MyBusinessLogic {

    private final Writer writer;

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

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

我们所拥有的是一些想要进行写作的业务逻辑。通过使用接口,我们的业务逻辑不再依赖于任何特定的写作方法。它只需要得到能够进行写作的对象即可。我们可以传递任何一个示例写手,并确信它会正常工作,因为我们关注的是写作行为,而不是实现方式。
通过这样做,业务逻辑不再依赖于文件系统、网络或其他任何东西。

-1

快速封装示例

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);
  ...

你只能访问“public”声明。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接