非抽象class的非空方法和抽象方法的抽象方法的好处?
Benefit of non-empty method of non-abstract class and abstract method of abstract method?
我不明白为什么我们使用抽象方法(抽象class),而我们可以使用非抽象class的空方法然后我们覆盖它。听起来不错吗?我正在寻求澄清这个问题。
我举2个例子
public abstract class MyClass {public abstract void foo();}
public MyChildClass extends MyClass {public void foo() {//..TODO}}
public class MyClass {public void foo(){//empty}}
public class MyChildClass extends MyClass {public void foo() {//..TODO}}
哪个更好?
我首先要说的是您应该尝试使用接口而不是抽象 classes。摘要 classes 将 subclass 耦合到 superclass 的实现。在像 Java 这样的语言中,subclass 可以覆盖任何方法,即使 superclass 并不打算这样做,而且大多数人不会用“不”来限定他们的方法一直覆盖”。
在最低级别,抽象方法在编译时为您提供两种具体保护:
- 他们强迫你重写子程序中的方法class
- 他们不允许创建摘要class
在列出抽象方法的用例之前,我只想说“通用功能”不是抽象基础的好理由 class。如果您需要通用功能,只需创建一个具有通用方法的 class,然后让各种 class 随意调用这些函数。
所以什么时候会使用摘要class?以下是一些示例:
模板方法
在模板方法模式中,您拥有所有的功能,但只有一个内部方面是多态的,所以您有子class重写该特定方面。
例如,如果你正在实现一个缓存,但是缓存失效策略是多态的,你可能有一个抽象的invalidate()
方法被其他方法在内部调用,但这取决于subclasses 实现 invalidate()
.
如果有首选的默认缓存失效策略,则 invalidate()
可以实施该默认值。但是,如果该默认值在某些情况下是彻头彻尾的破坏性,那么它不应该是默认值 - 它应该是抽象的,并且应该强制创建缓存的代码明确选择失效策略。
这也可以通过向构造函数传递一个Invalidator
class来实现(策略模式),但是如果失效逻辑需要调用缓存的方法,最好让那些方法保护并从子class(即模板方法模式)中调用它们。
其他方法的默认实现
在接口不能有默认方法的语言中(例如 Java 7),您可以使用抽象 classes 来模拟它。所有接口方法都是抽象的,但默认方法是常规 public 方法。
通用界面和功能
这只是模板方法模式的更通用版本。区别在于多态方法是 API.
的一部分
如果您的常用功能与您想要公开的功能有很多重叠,并且您不想要堆积如山的样板代码,则可以使用抽象 class。例如:
interface File {
abstract Buffer read(int size);
abstract void write(Buffer buf);
abstract long getSize();
abstract void setSize();
// ... get/set creation time, get/set modification time, get
// file type etc.
abstract long getOwner();
abstract void setOwner(long owner);
}
abstract class AbstractFile extends File {
DataMap dataMap;
MetadataMap metaMap;
protected getDiskMap() { return dataMap; }
protected getMetaMap() { return metaMap; }
public Buffer read(int size) { /* loop here */ }
public void write(Buffer buf) { /* loop here */ }
public long getSize() { /* logic */ }
public void setSize() { /* logic */ }
// ... implementation of get/set creation time, get/set modification
// time, get file type etc.
}
abstract class HardDriveFile extends AbstractFile {
OwnershipMap ownerMap;
abstract long getOwner() { /* logic */ }
abstract void setOwner(long owner) { /* logic */ }
}
abstract class ThumbDriveFile extends AbstractFile {
// thumb drives have no ownership
abstract long getOwner() { return 0; }
abstract void setOwner(long owner) { /* no-op */ }
}
abstract class SomeOtherfile extends AbstractFile {
...
}
如果我们砍掉中间人,让 HardDriveFile
和 ThumbDriveFile
(可能还有其他类型的文件)实现 File
并拼出所有常用方法,每个调用一个方法一些常见的 class,我们会得到堆积如山的样板文件。所以我们继承自一个抽象基础 class,它具有我们想要专门化的抽象方法(例如,基于所有权映射的存在)。
天真的做法是将 File
和 AbstractFile
组合成一个 class,这是您获得抽象方法 getOwner()
和setOwner()
,但最好将抽象 class 隐藏在实际接口后面,以防止 API 的消费者与抽象 class.
之间的耦合
我不明白为什么我们使用抽象方法(抽象class),而我们可以使用非抽象class的空方法然后我们覆盖它。听起来不错吗?我正在寻求澄清这个问题。
我举2个例子
public abstract class MyClass {public abstract void foo();}
public MyChildClass extends MyClass {public void foo() {//..TODO}}
public class MyClass {public void foo(){//empty}}
public class MyChildClass extends MyClass {public void foo() {//..TODO}}
哪个更好?
我首先要说的是您应该尝试使用接口而不是抽象 classes。摘要 classes 将 subclass 耦合到 superclass 的实现。在像 Java 这样的语言中,subclass 可以覆盖任何方法,即使 superclass 并不打算这样做,而且大多数人不会用“不”来限定他们的方法一直覆盖”。
在最低级别,抽象方法在编译时为您提供两种具体保护:
- 他们强迫你重写子程序中的方法class
- 他们不允许创建摘要class
在列出抽象方法的用例之前,我只想说“通用功能”不是抽象基础的好理由 class。如果您需要通用功能,只需创建一个具有通用方法的 class,然后让各种 class 随意调用这些函数。
所以什么时候会使用摘要class?以下是一些示例:
模板方法
在模板方法模式中,您拥有所有的功能,但只有一个内部方面是多态的,所以您有子class重写该特定方面。
例如,如果你正在实现一个缓存,但是缓存失效策略是多态的,你可能有一个抽象的invalidate()
方法被其他方法在内部调用,但这取决于subclasses 实现 invalidate()
.
如果有首选的默认缓存失效策略,则 invalidate()
可以实施该默认值。但是,如果该默认值在某些情况下是彻头彻尾的破坏性,那么它不应该是默认值 - 它应该是抽象的,并且应该强制创建缓存的代码明确选择失效策略。
这也可以通过向构造函数传递一个Invalidator
class来实现(策略模式),但是如果失效逻辑需要调用缓存的方法,最好让那些方法保护并从子class(即模板方法模式)中调用它们。
其他方法的默认实现
在接口不能有默认方法的语言中(例如 Java 7),您可以使用抽象 classes 来模拟它。所有接口方法都是抽象的,但默认方法是常规 public 方法。
通用界面和功能
这只是模板方法模式的更通用版本。区别在于多态方法是 API.
的一部分如果您的常用功能与您想要公开的功能有很多重叠,并且您不想要堆积如山的样板代码,则可以使用抽象 class。例如:
interface File {
abstract Buffer read(int size);
abstract void write(Buffer buf);
abstract long getSize();
abstract void setSize();
// ... get/set creation time, get/set modification time, get
// file type etc.
abstract long getOwner();
abstract void setOwner(long owner);
}
abstract class AbstractFile extends File {
DataMap dataMap;
MetadataMap metaMap;
protected getDiskMap() { return dataMap; }
protected getMetaMap() { return metaMap; }
public Buffer read(int size) { /* loop here */ }
public void write(Buffer buf) { /* loop here */ }
public long getSize() { /* logic */ }
public void setSize() { /* logic */ }
// ... implementation of get/set creation time, get/set modification
// time, get file type etc.
}
abstract class HardDriveFile extends AbstractFile {
OwnershipMap ownerMap;
abstract long getOwner() { /* logic */ }
abstract void setOwner(long owner) { /* logic */ }
}
abstract class ThumbDriveFile extends AbstractFile {
// thumb drives have no ownership
abstract long getOwner() { return 0; }
abstract void setOwner(long owner) { /* no-op */ }
}
abstract class SomeOtherfile extends AbstractFile {
...
}
如果我们砍掉中间人,让 HardDriveFile
和 ThumbDriveFile
(可能还有其他类型的文件)实现 File
并拼出所有常用方法,每个调用一个方法一些常见的 class,我们会得到堆积如山的样板文件。所以我们继承自一个抽象基础 class,它具有我们想要专门化的抽象方法(例如,基于所有权映射的存在)。
天真的做法是将 File
和 AbstractFile
组合成一个 class,这是您获得抽象方法 getOwner()
和setOwner()
,但最好将抽象 class 隐藏在实际接口后面,以防止 API 的消费者与抽象 class.