理解 OOP 中的抽象
Understanding abstraction in OOP
我正在研究面向对象的概念,抽象概念基本上被描述为对用户隐藏实现。因此,如果 class 中有一个成员函数,并且我们为某个任务调用该函数,抽象表示用户不应该关心事情是如何完成的,而应该只知道正在完成什么。但即使在非面向对象的编程风格中,如果我们编写一个函数,整个任务也可以通过简单地调用一个函数来完成。它不也遵循抽象逻辑吗?或者,OOP中的抽象和函数式编程有什么区别吗?
并不是您调用的函数提供了抽象,而是调用它的庄园。例如,您可能有一个允许您编写一行文本的函数:
void writeLine(string fileName, string value)
但这并不是从您正在写入文件的事实中抽象出来的。抽象版本不需要调用者提供 fileName
参数,因为它特定于函数的特定实现。相反,你会:
void writeLine(string value)
并且文件名是使用另一种机制提供的,例如class 的构造函数参数,如果您正在使用 OOP 并调用 writeLine 方法,或者在功能情况下,您可能会柯里化原始函数以创建抽象版本。
在面向对象编程中,我们通常从继承和多态的角度来思考抽象。
让我们考虑一下Writer
接口
interface Writer {
void write(byte[] bytes)
}
此界面允许用户写入...某物。什么,我们并不特别担心。我们可以有多个版本:
class FileWriter implements Writer
class StringWriter implements Writer
class LogWriter implements Writer
class MySuperCustomWriter implements Writer
我们在哪里写并不重要,可以是 File
、String
、套接字或任何地方。我们想要做的就是写一些东西。这让我们可以这样写代码:
public class MyBusinessLogic {
private final Writer writer;
public MyBusinessLogic(Writer writer) {
this.writer = writer;
}
void someBusinessLogic() {
// ..
writer.write(someStuff);
}
}
我们这里有一些需要编写的业务逻辑。通过使用接口,我们的业务逻辑不再依赖于任何特定的写法。它只是获取一些能够进行一些写入的对象。我们可以将它传递给我们的任何示例编写器,并确保它能正常工作,因为我们感兴趣的是编写的行为,而不是实现。
通过这样做,业务逻辑不依赖于文件系统、网络或其他任何东西。
快速封装示例
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" 声明。
我正在研究面向对象的概念,抽象概念基本上被描述为对用户隐藏实现。因此,如果 class 中有一个成员函数,并且我们为某个任务调用该函数,抽象表示用户不应该关心事情是如何完成的,而应该只知道正在完成什么。但即使在非面向对象的编程风格中,如果我们编写一个函数,整个任务也可以通过简单地调用一个函数来完成。它不也遵循抽象逻辑吗?或者,OOP中的抽象和函数式编程有什么区别吗?
并不是您调用的函数提供了抽象,而是调用它的庄园。例如,您可能有一个允许您编写一行文本的函数:
void writeLine(string fileName, string value)
但这并不是从您正在写入文件的事实中抽象出来的。抽象版本不需要调用者提供 fileName
参数,因为它特定于函数的特定实现。相反,你会:
void writeLine(string value)
并且文件名是使用另一种机制提供的,例如class 的构造函数参数,如果您正在使用 OOP 并调用 writeLine 方法,或者在功能情况下,您可能会柯里化原始函数以创建抽象版本。
在面向对象编程中,我们通常从继承和多态的角度来思考抽象。
让我们考虑一下Writer
接口
interface Writer {
void write(byte[] bytes)
}
此界面允许用户写入...某物。什么,我们并不特别担心。我们可以有多个版本:
class FileWriter implements Writer
class StringWriter implements Writer
class LogWriter implements Writer
class MySuperCustomWriter implements Writer
我们在哪里写并不重要,可以是 File
、String
、套接字或任何地方。我们想要做的就是写一些东西。这让我们可以这样写代码:
public class MyBusinessLogic {
private final Writer writer;
public MyBusinessLogic(Writer writer) {
this.writer = writer;
}
void someBusinessLogic() {
// ..
writer.write(someStuff);
}
}
我们这里有一些需要编写的业务逻辑。通过使用接口,我们的业务逻辑不再依赖于任何特定的写法。它只是获取一些能够进行一些写入的对象。我们可以将它传递给我们的任何示例编写器,并确保它能正常工作,因为我们感兴趣的是编写的行为,而不是实现。
通过这样做,业务逻辑不依赖于文件系统、网络或其他任何东西。
快速封装示例
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" 声明。