public interface iUserInfo
{
string getUserName(int userId);
string getUserAge(string username);
}
public class UserDb implements iUserInfo
{
string getUserName(int userId)
{
//code to retrieve user info from the database
}
}
public class UserTxtFile implements iUserInfo
{
string getUserName(int userId)
{
//code to retrieve user info from the plain text file
}
}
public class UserManager()
{
private iUserInfo userInfo;
public string retrieveUserData(string userName)
{
return userInfo.getUserAge(userName);
}
}
我的教授说
“上面的用户管理器类有一个类型的引用变量 iUserInfo,因此,如果此引用由工厂方法提供或由 任何其他方式,经理类的代码将是相同的 无论提供什么实现。
如果需要某些实现,这种方法可以最大限度地提高灵活性 在软件开发过程中被更改为更高级别 层根本不会受到影响。“
我需要了解两件事;灵活性最大化和工厂方法。
后者我猜它可能是这样的
public class UserManager()
{
private iUserInfo userInfo;
private UserTxtFile uTxtFile;
private UserDb uDB;
public iUserInfo GetUserInfoObjFactory(bool dbOrTxt)
{
return dbOrTxt? uTxtFile:uDB;
}
public string retrieveUserData(string userName)
{
return userInfo.getUserAge(userName);
}
}
在演讲期间,我对其他事情做了一些白日梦,现在无法弄清楚究竟是什么意思?我希望能够更深入地了解一些情况,以防一些采访者可能会用更开放的问题来攻击我不确定如何回答。
当高级别图层受到影响时,您是否还可以在示例情况下添加一些会破坏上述来源的代码?非常感谢你。
[更新] 我也找到了一个答案,例如“我们需要接口来分组常用方法”并不是很有说服力。仅抽象基类也有帮助。 例如,来自2个对象{Penguin vs Bird}的Fly行为。我不能简单地告诉面试官,根本需要IFly。即使我有数百万个需要Fly的不同对象,我也可以为每个对象实现每个Fly。因为即使我设计了IFly,我仍然需要在实现类中执行此操作。您能否提供更多细节案例或重新解释界面必须如何?
答案 0 :(得分:1)
关于接口的需求。
我们假设您必须实施机场调度员。它与机场空间中的物体相互作用,给予它们关于它们的位置和空气状况的命令。这组命令是明确定义的,它不(不应该)依赖于空间中对象的类型。调度员需要知道位置,他必须能够允许着陆,拒绝着陆,延迟着陆并发出紧急进程校正。为了尽可能容易地实现和维护,我们定义了一种在所有“飞机”中实现的接口。
然后我们可以创建一个AirportDispatcher
,它将以一般方式实现我们的界面的一组对象,而不知道它是如何实现的(直升机可以等到位,但飞机需要在等待时绕行)甚至是飞机的类型。你应该知道的是,它飞得很快,它可以服从你的命令。
请注意,就Java而言,它不是interface
,它只是一个契约,我们如何实现它只取决于语言的可能性。动态或鸭式语言可以在没有任何语法约束的情况下完成,而静态类型语言则需要一些额外的工作。
接口和抽象类。接口和抽象类之间的主要区别在于,对于一个对象可能有多个接口,但只有一个抽象类(不是它在所有语言中的方式,但这是它的共同点)。此外,接口可以不包含实现,而抽象类可以。
答案 1 :(得分:1)
我会稍微更改你的代码以便解释
以下是系统中的一段代码,用于打印用户名。更重要的是,让我们的图像不仅仅有一个,而是直接使用UserDb
的一百个代码,它们会在你的系统中随处传播。
public void printUserName(String userId) {
UserDb db = getUserDb();
System.out.println(db.getUserName(userId));
}
现在,如果我们想从文本文件中检索用户信息怎么办?执行此操作的唯一方法是将使用UserDb
的所有代码更改为UserTextFile
。它需要花费很多时间,并且它可以很容易地引入错误,因为我们可能会意外地改变我们不应该做的事情。
我们称这些代码为UserDb
Coupling。
所以我们有UserManager
来解决这个问题
public class UserManager {
private UserDb db;
public UserManager(UserDb db) {
this db = db;
}
public String getUserName(String userId) {
// return user name using UserDb
}
}
如果我们系统中的所有代码都使用UserManager
作为检索用户名的方式。当我们想切换到文本文件时,我们所能做的就是更改UserManager
内的代码。
但是,在现实世界的编码中,UserManager
不能那么简单,它们也可能还有其他责任,例如在查询之前验证输入。我们可能仍会引入错误。
这就是为什么我们需要另一层来一次性删除这个耦合。
这是一个界面
public interface iUserInfo {
public String getUserName(int userId);
public String getUserAge(string username);
}
...我们让UserManager
取决于iUserInfo
public class UserManager {
private iUserInfo info;
public UserManager(iUserInfo info) {
this info = info;
}
public String getUserName(String userId) {
// return user name using info
}
}
现在,每当我们想要将UserDb
更改为UserTextFile
时,我们所做的就是编写一个新的具体类iUserInfo
。 UserManager
永远不会注意到,因为它不需要知道实施细节。
我们修改的代码越少,引入错误的机会就越少。这就是我们想要这种灵活性的原因。
此技术称为Inversion of Control。
工厂方法是处理对象创建的设计模式之一。查看此问题以获取更多信息
Factory, Abstract Factory and Factory Method
你的职业提到工厂方法的原因,是因为这些模式用于隐藏对象的创建知识,在这种情况下,iUserInfo
来自其他类,这意味着这些方法/类是唯一的代码与具体课程。
因此,我们可以最大限度地减少更改iUserInfo
接口使所有具体类无需扩展即可。这很好,因为在Java中,您只能从一个类继承。
另一方面,抽象类使您更容易处理在不同实现之间使用的公共代码。
您可以查看此问题以获取更多详细信息 Interface vs Base class
答案 2 :(得分:0)
您的教授讲座意味着loose coupling,示例涉及Bridge PAttern。
更多细节:
UserManager
类必须处理应用业务逻辑和规则的数据,保存并检索它。这个类必须只知道它必须保存和检索来往某个地方的数据,但它不负责做脏工作。相反,您将拥有一个对象iUserInfo
实例,它将处理数据的保存和检索。这个对象将如何完成这项工作?它与UserManager
类无关。这是松耦合。
现在,为什么必须是处理这项工作的接口/抽象类?因为您可以拥有不同的数据源:文本文件,xml文件,关系数据库,内存数据库,外部设备等。您的UserManager
类不能直接调用数据源处理程序,而是另一个类将提供这种确切的实现。对于这种情况,您必须使用由抽象类或接口处理的抽象。抽象类和接口是建立客户端可以使用的方法的契约,但不一定是方法实现。这怎么工作?我们来看一个例子:
public interface iUserInfo {
//the client (any class that uses the interface) knows that can get the UserName
//and the UserAge, it doesn't need to know how...
string getUserName(int userId);
string getUserAge(string username);
}
public class UserDb implements iUserInfo
{
string getUserName(int userId)
{
//this implementation will connect to database to get the data
}
}
public class UserTxtFile implements iUserInfo
{
string getUserName(int userId)
{
//this implementation will read from a text file to get the data
}
}
到目前为止,UserDb
和UserTxtFile
类将处理脏工作。现在,您的UserManager
可以吸一口气,因为它无法知道数据的存储位置。但还有另一个问题:UserManager
应该使用哪种实现来获取数据?
工厂方法是Factory Method Pattern的一个实现,它声明客户端必须不知道它应该使用哪个实现,而工厂将根据配置或其他业务规则做出决定。这意味着,UserManager
类可以安静地休息,因为它不必定义访问既不是实现!听起来不错吧?现在,举例来说,只有一个简单的方法可以作为工厂方法
//it will return the iUserInfo implementation based in a boolean variable
//this could become more complex depending on the rules, like retrieving the boolean
//value from a configuration file
public iUserInfo GetUserInfoObjFactory(bool dbOrTxt)
{
return dbOrTxt? uTxtFile:uDB;
}
更多信息: