4
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,我仍然需要在实现类中这样做。请您提供更详细的案例或重新解释接口如何成为必须的?

4

3 回答 3

1

关于接口的需要。

假设您必须实现机场调度程序。它与机场空间中的物体相互作用,向它们发出有关其位置和空中状况的命令。这组命令是明确定义的,它不(不应该)依赖于空间对象的类型。调度员需要知道位置,他必须能够允许着陆、拒绝着陆、延迟着陆和发出紧急航向修正。为了尽可能容易地实现和维护这一点,我们定义了一种我们在所有“飞机”中实现的接口。

然后我们可以创建一个AirportDispatcher,它将与以通用方式实现我们接口的一组对象一起工作,而不知道它是如何实现的(直升机可以在原地等待,但飞机需要在等待时四处走动)甚至飞机的类型。你应该知道的是它会飞,它可以服从你的命令。

请注意,它不是interfaceJava 术语,它只是一个合约,我们如何实现它仅取决于语言的可能性。动态或鸭子类型的语言可以在没有任何语法限制的情况下做到这一点,而静态类型的语言则需要一些额外的工作。

有接口和抽象类。接口和抽象类之间的主要区别在于,一个对象可能有多个接口,但只有一个抽象类(并非所有语言都这样,但这是共同点)。接口也不能包含实现,而抽象类可以

于 2012-08-05T08:57:15.677 回答
1

我将稍微更改您的代码以进行解释

为什么是灵活性?

这是您系统中的一段代码,它将打印用户名。更重要的是,让我们想象一下,直接使用的不仅仅是一个,而是数百个代码UserDb,它们在你的系统中到处传播。

public void printUserName(String userId) {
  UserDb db = getUserDb();
  System.out.println(db.getUserName(userId));
}

现在,如果我们想从文本文件中检索用户信息怎么办?做到这一点的唯一方法是将所有使用的代码更改UserDbUserTextFile. 这将花费大量时间,并且很容易引入错误,因为我们可能会意外更改不应该更改的内容。

我们称这些代码UserDb耦合

所以这里我们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
  }
}

现在,每当我们想要更改UserDbUserTextFile时,我们所做的就是编写一个新的具体类iUserInfoUserManager永远不会注意到,因为它不需要知道实现的细节。

我们修改的代码越少,我们引入错误的机会就越少。这就是我们想要这种灵活性的原因。

这种技术称为控制反转


工厂方法 ?

工厂方法是处理对象创建的设计模式之一。检查此问题以获取更多信息

工厂、抽象工厂和工厂方法

您的专业之所以提到工厂方法,是因为这些模式用于隐藏对象的创建知识,iUserInfo在这种情况下,对于其他类,这意味着这些方法/类是唯一与具体类耦合的代码。

所以我们可以最小化改变实施的影响iUserInfo

接口与抽象类?

接口使您所有的具体类都可以在没有扩展它的情况下工作。这很好,因为在 Java 中,您只能从一个类继承。

另一方面,抽象类使您更容易处理在不同实现之间使用的公共代码。

您可以查看此问题以获取更多详细信息 接口与基类

于 2012-08-05T09:39:50.787 回答
0

您的教授讲座是关于松散耦合的,示例指的是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
    }
}

到现在为止,UserDbandUserTxtFile类将处理这些脏活。现在,您UserManager可以喘口气了,因为它一定不知道数据将存储在哪里。但是还有另一个问题:应该UserManager使用哪个实现来获取数据?

工厂方法是工厂方法模式的一个实现,它声明客户端不能知道它应该使用哪个实现,相反,工厂将根据配置或其他业务规则做出决定。这意味着,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;
}

更多信息:

于 2012-08-05T08:51:18.287 回答