- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
有人可以帮助新手程序员了解他的解决方案是否正确吗?
我的问题类似于下面两个:
What's wrong with overridable method calls in constructors?
Factory pattern in C#: How to ensure an object instance can only be created by a factory class?
问题: 我想要的子类仅在初始化方法上有所不同。但是,我也想防止在没有初始化的情况下实例化这些类。换句话说,我想确保某些“initialize()”方法将总是在子类实例化后被调用:
public abstract class Data {
protected Parameter dataSource;
Data(parameter1){
this.dataSource = parameter1;
loadData(); // should be called to initialise class fields and ensure correct work of other class methods
}
protected abstract loadData(){
... //uses dataSource
}
}
所以我决定对构造函数进行初始化。它一直有效(现在我知道这是一个非常糟糕的做法),直到我创建了一个子类,其中初始化方法使用了一些额外的参数:
public class DataFromSpecificSources extends Data {
private Parameter dataSource2;
public DataFromSpecificSources(parameter1, parameter2){
this.dataSource2 = parameter2; // I can't put it here because the constructor is not called yet
super(parameter1); // this, of course, will not work
}
@Override
private void loadData(){
... // uses both dataSource 1 and 2
// or just dataSource2
}
}
当然,这是行不通的。然后我开始寻找正确的模式......在阅读之前发布的问题的答案后,我决定使用工厂并将子类构造函数的可见性限制在包中:
我的解决方案:
// factory ensures that loadData() method will be called
public class MyDataFactory(){
public Data createSubClass(parameter1,parameter2){
Data subClass;
if (parameter2 != null){
subClass = new DataFromSpecificSources(parameter1, parameter2);
subClass.loadData();
} else {
subClass = new AnotherSubClass(parameter1);
subClass.loadData()
}
return subClass;
}
}
public abstract class Data {
protected Parameter dataSource;
Data(parameter1){
this.dataSource = parameter1;
}
// I don't call it in constructor anymore - instead it's controlled within the factory
protected abstract loadData(){
... //uses dataSource
}
}
public class DataFromSpecificSources {
private Parameter dataSource2;
protected DataFromSpecificSources(){}
// now this constructor is only visible within package (only for the factory in the same package)
DataFromSpecificSources(parameter1, parameter2){
super(parameter1); // it does not initialise data anymore
this.dataSource2 = parameter2;
}
@Override
protected void loadData(){
... // uses dataSources 1 and 2
}
}
现在工厂确保子类将被初始化(数据将被加载)并且不允许在其他包中实例化子类。其他类无法访问子类的构造函数,被迫使用工厂来获取子类的实例。
我只是想问一下我的解决方案是否正确(逻辑上),并且子类构造函数可见性仅限于包的工厂方法在这里是正确的选择吗?!还是有其他更有效的模式解决问题?!
最佳答案
使用工厂绝对是朝着正确方向迈出的一步。我看到的问题是,当您想添加带有第三个参数的第三个类时会发生什么。现在您的 Factory 要么必须有第二个重载的 createSubClass
方法接受第三个参数,要么您的所有代码都必须重写以提供第三个参数。此外,你强制任何使用工厂的人为第二个参数指定 null
,即使他们只想要单个参数类......当你到达需要 15 个参数的类时,你将如何记住哪个参数是哪个
对此的解决方案是改用构建器模式。
public class MyDataBuilder(){
private parameter1 = null;
private parameter2 = null;
public MyDataBuilder withParameter1(parameter1) {
this.parameter1 = parameter1;
return this;
}
public MyDataBuilder withParameter2(parameter2) {
this.parameter2 = parameter2;
return this;
}
public Data createSubClass(){
Data subClass;
if (parameter2 != null){
subClass = new DataFromSpecificSources(parameter1, parameter2);
} else {
subClass = new AnotherSubClass(parameter1);
}
subClass.loadData();
return subClass;
}
}
现在创建数据实例的代码可以像这样工作:
Data data = new MyDataBuilder().withParameter1(param1).withParameter2(param2).create();
或
Data data = new MyDataBuilder().withParameter1(param1).create();
当您添加 parameter3 时,该代码是面向 future 的......如果您需要的话,您甚至可以让构建器为 parameter3 设置非空默认值。
接下来您会注意到,您现在拥有了一个包含所有必需参数的漂亮 Builder 对象...所以现在您可以将 getter 添加到 Builder 并将 Builder 作为构造函数参数传递,例如
public class DataFromSpecificSources {
...
DataFromSpecificSources(MyDataBuilder builder){
...
}
...
}
所以你现在几乎有了一个标准的构造函数签名
现在进行一些特定于 Java 的改进。我们可以让构建器根本不需要知道子类!
使用 DI 框架,我们可以将实现 Data
接口(interface)/抽象类的类注入(inject)到 Builder 中,然后遍历每个类,直到找到支持 Builder 实例配置的类.
穷人的 DI 框架是自 JRE 1.6 以来可用的 /META-INF/services
契约和 ServiceLoader
类(尽管核心逻辑从 Java 开始1.2)
您的构建器的创建方法看起来有点像
public Data create() {
for (DataFactory factory: ServiceLoader.load(DataFactory.class)) {
if (factory.canCreate(this)) {
Data result = factory.newInstance(this);
result.loadData();
return result;
}
}
throw new IllegalStateException("not even the default instance supports this config");
}
你是否想走到那个极端是值得怀疑的......但由于你可能会在某个时间点查看其他人的代码时遇到它,所以现在可能是向你指出它的好时机。
哦,之所以要加一个Factory类供ServiceLoader查找,是因为ServiceLoader期望调用默认构造函数,而我们隐藏了默认构造函数,所以我们用一个Factory类来做我们并允许我们隐藏构造函数。
没有什么可以阻止工厂类成为数据类中的静态内部类(这使它们能够很好地了解它们正在创建的类),例如
public class UberData extends Data {
private UberData(MyDataBuilder config) {
...
}
public static class Factory extends DataFactory {
protected Data create(MyDataBuilder config) {
return new UberData(config);
}
protected boolean canCreate(MyDataBuilder config) {
return config.hasFlanges() and config.getWidgetCount() < 7;
}
}
}
然后我们可以在 META-INF/services/com.mypackage.DataFactory
中列出
com.mypackage.UberData.Factory
com.mypackage.DataFromSpecificSources.Factory
com.some.otherpackage.AnotherSubClass.Factory
这种解决方案最好的一点是它允许添加额外的实现,只需在运行时将这些实现添加到类路径中......即非常松散的耦合
关于java - 在不调用所需的初始化方法的情况下防止子类实例化的正确方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12427131/
我是 Java 新手,这是我的代码, if( a.name == b.name && a.displayname == b.displayname && a.linknam
在下面的场景中,我有一个 bool 值。根据结果,我调用完全相同的函数,唯一的区别是参数的数量。 var myBoolean = ... if (myBoolean) { retrieve
我是一名研究 C++ 的 C 开发人员: 我是否正确理解如果我抛出异常然后堆栈将展开直到找到第一个异常处理程序?是否可以在不展开的情况下在任何 throw 上打开调试器(即不离开声明它的范围或任何更高
在修复庞大代码库中的错误时,我观察到一个奇怪的情况,其中引用的动态类型从原始 Derived 类型更改为 Base 类型!我提供了最少的代码来解释问题: struct Base { // some
我正在尝试用 C# 扩展给定的代码,但由于缺乏编程经验,我有点陷入困境。 使用 Visual Studio 社区,我尝试通过控制台读出 CPU 核心温度。该代码使用开关/外壳来查找传感器的特定名称(即
这可能是一个哲学问题。 假设您正在向页面发出 AJAX 请求(这是使用 Prototype): new Ajax.Request('target.asp', { method:"post", pa
我有以下 HTML 代码,我无法在所有浏览器中正常工作: 我试图在移动到
我对 Swift 很陌生。我如何从 addPin 函数中检索注释并能够在我的 addLocation 操作 (buttonPressed) 中使用它。我正在尝试使用压力触摸在 map 上添加图钉,在两
我设置了一个详细 View ,我是否有几个 Nib 文件根据在 Root View Controller 的表中选择的项目来加载。 我发现,对于 Nibs 的类,永远不会调用 viewDidUnloa
我需要动态访问 json 文件并使用以下代码。在本例中,“bpicsel”和“temp”是变量。最终结果类似于“data[0].extit1” var title="data["+bpicsel+"]
我需要使用第三方 WCF 服务。我已经在我的证书存储中配置了所需的证书,但是在调用 WCF 服务时出现以下异常。 向 https://XXXX.com/AHSharedServices/Custome
在几个 SO 答案(1、2)中,建议如果存在冲突则不应触发 INSERT 触发器,ON CONFLICT DO NOTHING 在触发语句中。也许我理解错了,但在我的实验中似乎并非如此。 这是我的 S
如果进行修改,则会给出org.hibernate.NonUniqueObjectException。在我的 BidderBO 类(class)中 @Override @Transactional(pr
我使用 indexOf() 方法来精细地查找数组中的对象。 直到此刻我查了一些资料,发现代码应该无法正常工作。 我在reducer中尝试了上面的代码,它成功了 let tmp = state.find
假设我有以下表格: CREATE TABLE Game ( GameID INT UNSIGNED NOT NULL, GameType TINYINT UNSIGNED NOT NU
代码: Alamofire.request(URL(string: imageUrl)!).downloadProgress(closure: { (progress) in
我是一名优秀的程序员,十分优秀!