根据对象类型调用方法
Call a method based on the object type
我有两个辅助方法:
public String load(URL url) {...}
public String load(File file) {...}
我想要一个根据接收到的对象类型调用适当的辅助方法的方法:
public void operate(Object object) {...}
我知道有一种复杂的方法是:
public void operate(Object object) {
String data = null;
if (object.getClass().equals(File.class)) {
data = load((File) object);
}
if (object.getClass().equals(URL.class)) {
data = load((URL) object);
}
// operate on the data....
}
然而,这看起来并不优雅,很好奇是否有更好的方法..
您可以使用 instanceof 并检查它,然后转换对象并调用方法:
if (obj instanceof File) {
((File) obj).method();
}
else if (obj instanceof URL) {
((URL) obj).method();
}
或者像这样的逆:
if (obj instanceof File) {
load((File) obj)
}
else if (obj instanceof URL) {
load((URL) obj)
}
你是对的:转换是必要的,但不够优雅。
如果您喜欢 GoF 设计模式,另一种方法是访问者模式,也称为双重分派。
第三种方法是使用Java反射。
稍微不那么复杂的方式是instanceof
,例如
if (object instanceof File)) {
data = load((File) object);
}
但是,大多数情况下,使用 instanceof
表示您有更好的结构来实现您想要实现的目标,例如
public void operate(File file) {
operate(load(file));
}
public void operate(URL url) {
operate(load(url));
}
public void operate(String data) {
// operate on the data....
}
同时重载操作方法。使用您收到的数据调用接受字符串的方法。
public static void operate(URL url) {
String data = load(url);
doOperations(data);
}
public static void operate(File file) {
String data = load(file);
doOperations(data);
}
private static void doOperations(String data) {
//TODO Do something with data
}
However, this does not seem elegant and was curious if there was a better way.
没错。这违反了 Open-Closed 原则。 class 必须对扩展开放但对修改关闭。当您说您需要一个通用对象时,您也是正确的。您可以执行以下操作:
创建一个Loader接口
public interface Loader<T> {
public String load(T t);
}
创建从文件加载的加载器
public class FileLoader implements Loader<File> {
public String load(File f) {
//load from file
}
}
创建加载程序以从 Url
加载
public class UrlLoader implements Loader<Url> {
public String load(URL r) {
//load from url
}
}
创建一个对数据进行操作的class
class DataOperator<T> {
Loader<T> loader;
public SomeClient(Loader<T> loader) {
this.loader = loader;
}
public void operate(T inputSource) {
String data = loader.load(inputSource);
//operate on the data
}
}
客户端代码就可以使用上面的API如下图:
DataOperator<File> fileDataOperator = new DataOperator<>(new FileLoader());
fileDataOperator.operate(new File("somefile.txt"));
DataOperator<URL> urlDataOperator = new DataOperator<>(new UrlLoader());
urlDataOperator.operate(new URL("http://somesite.com"));
您可能会想,为了解决一个简单的问题,需要很多 classes。然而,这实际上符合众所周知的Open-Closed设计原则。请注意如何通过创建适当 class 的实例来控制用于加载数据的技术。您获得的另一个优势是,您可以通过创建接受用户输入并创建适当的具体子 class 的 Factory
来决定在 runtime
使用哪种技术。这是Strategy pattern的简化版。
免责声明: 由于我在这台机器上没有Java
,所以上面提供的代码示例没有经过编译错误测试。
我只是偶然发现了同样的问题,并找到了使用反射的不同方法:
public void operate(Object object) {
Method method = this.getClass().getDeclaredMethod("load", object.getClass());
String data = (String) method.invoke(this, object);
// [...]
}
当然,它伴随着处理相当多异常的负担。
我有两个辅助方法:
public String load(URL url) {...}
public String load(File file) {...}
我想要一个根据接收到的对象类型调用适当的辅助方法的方法:
public void operate(Object object) {...}
我知道有一种复杂的方法是:
public void operate(Object object) {
String data = null;
if (object.getClass().equals(File.class)) {
data = load((File) object);
}
if (object.getClass().equals(URL.class)) {
data = load((URL) object);
}
// operate on the data....
}
然而,这看起来并不优雅,很好奇是否有更好的方法..
您可以使用 instanceof 并检查它,然后转换对象并调用方法:
if (obj instanceof File) {
((File) obj).method();
}
else if (obj instanceof URL) {
((URL) obj).method();
}
或者像这样的逆:
if (obj instanceof File) {
load((File) obj)
}
else if (obj instanceof URL) {
load((URL) obj)
}
你是对的:转换是必要的,但不够优雅。
如果您喜欢 GoF 设计模式,另一种方法是访问者模式,也称为双重分派。
第三种方法是使用Java反射。
稍微不那么复杂的方式是instanceof
,例如
if (object instanceof File)) {
data = load((File) object);
}
但是,大多数情况下,使用 instanceof
表示您有更好的结构来实现您想要实现的目标,例如
public void operate(File file) {
operate(load(file));
}
public void operate(URL url) {
operate(load(url));
}
public void operate(String data) {
// operate on the data....
}
同时重载操作方法。使用您收到的数据调用接受字符串的方法。
public static void operate(URL url) {
String data = load(url);
doOperations(data);
}
public static void operate(File file) {
String data = load(file);
doOperations(data);
}
private static void doOperations(String data) {
//TODO Do something with data
}
However, this does not seem elegant and was curious if there was a better way.
没错。这违反了 Open-Closed 原则。 class 必须对扩展开放但对修改关闭。当您说您需要一个通用对象时,您也是正确的。您可以执行以下操作:
创建一个Loader接口
public interface Loader<T> {
public String load(T t);
}
创建从文件加载的加载器
public class FileLoader implements Loader<File> {
public String load(File f) {
//load from file
}
}
创建加载程序以从 Url
加载public class UrlLoader implements Loader<Url> {
public String load(URL r) {
//load from url
}
}
创建一个对数据进行操作的class
class DataOperator<T> {
Loader<T> loader;
public SomeClient(Loader<T> loader) {
this.loader = loader;
}
public void operate(T inputSource) {
String data = loader.load(inputSource);
//operate on the data
}
}
客户端代码就可以使用上面的API如下图:
DataOperator<File> fileDataOperator = new DataOperator<>(new FileLoader());
fileDataOperator.operate(new File("somefile.txt"));
DataOperator<URL> urlDataOperator = new DataOperator<>(new UrlLoader());
urlDataOperator.operate(new URL("http://somesite.com"));
您可能会想,为了解决一个简单的问题,需要很多 classes。然而,这实际上符合众所周知的Open-Closed设计原则。请注意如何通过创建适当 class 的实例来控制用于加载数据的技术。您获得的另一个优势是,您可以通过创建接受用户输入并创建适当的具体子 class 的 Factory
来决定在 runtime
使用哪种技术。这是Strategy pattern的简化版。
免责声明: 由于我在这台机器上没有Java
,所以上面提供的代码示例没有经过编译错误测试。
我只是偶然发现了同样的问题,并找到了使用反射的不同方法:
public void operate(Object object) {
Method method = this.getClass().getDeclaredMethod("load", object.getClass());
String data = (String) method.invoke(this, object);
// [...]
}
当然,它伴随着处理相当多异常的负担。