我有两种方法可以在我的应用程序中保存数据:保存到数据库并保存到文件。因为我不希望客户端代码处理对象的构造,所以我创建了一个类(我的理解)是带有工厂方法的简单工厂。代码如下:
public static DataPersister createDataPersister(Boolean saveToDb, Session session, String filename) {
if (saveToDb) {
return new DatabaseDataPersister(session);
} else {
return new FileDataPersister(filename);
}
}
使用此设置客户端代码不必处理构造任何内容或决定是否保存到DB或文件 - 它可以只调用工厂返回的对象上的save()
方法,如下所示:
DataPersister dataPersister = DataPersisterSimpleFactory.createDataPersister(this.savetoDb, this.session, this.filename);
dataPersister.save(this.data);
我的问题是 - 这个解决方案打破了SOLID原则吗?为了创造例如DatabaseDataPersister
客户端代码需要传递filename
参数,DataPersister
的此实现将不会使用它。我觉得它不适合与接口隔离原理类似的东西,但不是那么。
如果解决方案确实是代码味道 - 我该如何清理?
答案 0 :(得分:1)
你已经传递了一堆与各种持久性无关的东西。
就目前而言,您需要一种方法,该方法需要Session
,而且需要String
并且您已完成。不需要布尔值,不需要无用的参数。这可以帮助您做出决策,而且没有任何瑕疵。
这是不是一个好主意......我很矛盾。你没有储蓄太多;也许只是在每种类型中都有一个静态工厂,因此它在代码中明确表示您正在创建的类型。
考虑当您添加一个新的持久性(如REST端点)时会发生什么,该持久性将带有一个URL(可能是一个字符串,可能是一个实际的URL)。您现在甚至需要更多无用的参数等。或者您可以从头开始传递URI,例如file://
或http://
并解决该问题。
有很多方法可以做到这一点 - 我不相信那里有一个明显正确的"回答,可能归结为意见。
答案 1 :(得分:1)
这是使用工厂模式的绝佳机会
interface DataPersister {
void persist(String s);
}
private class DatabasePersister implements DataPersister {
final Session session;
public DatabasePersister(Session session) {
this.session = session;
}
@Override
public void persist(String s) {
System.out.println("Persist to database: " + s);
}
}
private class FilePersister implements DataPersister {
final String filename;
public FilePersister(String filename) {
this.filename = filename;
}
@Override
public void persist(String s) {
System.out.println("Persist to file: " + s);
}
}
class PersisterFactory {
public DataPersister createDatabasePersister(Session session) {
return new DatabasePersister(session);
}
public DataPersister createFilePersister(String filename) {
return new FilePersister(filename);
}
}
public void test(String[] args) {
DataPersister databasePersister = new PersisterFactory().createDatabasePersister(new Session());
databasePersister.persist("Hello");
DataPersister filePersister = new PersisterFactory().createFilePersister("Hello");
filePersister.persist("Hello");
}
答案 2 :(得分:1)
我认为违反的SOLID原则是DIP。
您的客户端类必须直接依赖静态工厂,对实际实现DatabaseDataPersister
和FileDataPersister
具有编译时依赖性,而不仅仅是抽象DataPersister
。
要解决这个问题,请向客户提供您希望他们使用的DataPersister
。构造函数通常是一个很好的地方:
public class ExampleClient {
private final DataPersister dataPersister;
public ExampleClient(DataPersister dataPersister) {
this.dataPersister = dataPersister;
}
public void methodThatUsesSave(){
dataPersister.save(data);
}
}
此代码在没有具体实现的情况下编译,即它不依赖于它们。客户端也不需要知道filename
或session
,因此它也解决了代码异味。
我们可以决定在施工时给出哪个具体实施,这里我使用你现有的方法:
DataPersister dataPersister = DataPersisterSimpleFactory.createDataPersister(this.savetoDb, this.session, this.filename);
ExampleClient example = new ExampleClient(dataPersister);
答案 3 :(得分:0)
这里正确的解决方案是将weston的依赖注入与OldCurmudgeon的工厂模式结合起来。
public class ExampleClient {
private final DataPersister dataPersister;
public ExampleClient(DataPersister dataPersister) {
this.dataPersister = dataPersister;
}
public void methodThatUsesSave(){
dataPersister.save(data);
}
}
class PersisterFactory {
public DataPersister createDatabasePersister(Session session) {
return new DatabasePersister(session);
}
public DataPersister createFilePersister(String filename) {
return new FilePersister(filename);
}
}
上层代码:
PersisterFactory = new PersisterFactory();
DataPersister dataPersister;
if (saveToDb)
dataPersister = PersisterFactory.createDatabasePersister(new Session());
else
dataPersister = PersisterFactory.createFilePersister("Hello");
ExampleClient example = new ExampleClient(dataPersister);
通常dataPersister
来自DI容器,saveToDb
来自配置,但是测试当然是一个例外。