使用对象包装类的灵活性

时间:2012-08-05 08:24:09

标签: c# java oop design-patterns

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,我仍然需要在实现类中执行此操作。您能否提供更多细节案例或重新解释界面必须如何?

3 个答案:

答案 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时,我们所做的就是编写一个新的具体类iUserInfoUserManager永远不会注意到,因为它不需要知道实施细节。

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

此技术称为Inversion of Control


工厂方法?

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

Factory, Abstract Factory and Factory Method

你的职业提到工厂方法的原因,是因为这些模式用于隐藏对象的创建知识,在这种情况下,iUserInfo来自其他类,这意味着这些方法/类是唯一的代码与具体课程。

因此,我们可以最大限度地减少更改iUserInfo

实施的影响

界面vs.抽象类?

接口使所有具体类无需扩展即可。这很好,因为在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
    }
}

到目前为止,UserDbUserTxtFile类将处理脏工作。现在,您的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;
}

更多信息: