为什么我们需要通过InterfaceFactory来获取对象而不是只传递具体的对象
Why we need to pass the InterfaceFactory to get the object rather than just pass the specific object
"Thinking In Java"中有一个Interface and Factories
的例子如下:
(1) 定义游戏和 GameFactory 接口
interface Game { boolean move(); }
interface GameFactory { Game getGame(); }
(2)写出它们的实现
class Checkers implements Game {
private int moves = 0;
private static final int MOVES = 3;
public boolean move() {
System.out.println("Checkers move " + moves);
return ++moves != MOVES;
}
}
class CheckersFactory implements GameFactory {
public Game getGame() { return new Checkers(); }
}
class Chess implements Game {
private int moves = 0;
private static final int MOVES = 4;
public boolean move() {
print("Chess move " + moves);
return ++moves != MOVES;
}
}
class ChessFactory implements GameFactory {
public Game getGame() { return new Chess(); }
}
(3) 和客户端代码
public class Games {
public static void playGame(GameFatory factory) {
Game s = factory.getGame();
while (s.move());
}
public static void main(String[] args) {
playGame(new CheckersFactory());
playGame(new ChessFactory());
}
}
那么对于客户端代码,我们何必把GameFactory
传给playGame
的函数呢?
我们可以直接设置Game
作为参数如下:
public class Games {
public static void playGame(Game game) {
while (game.move());
}
public static void main(String[] args) {
playGame(new Checkers());
playGame(new Chess());
}
}
因此我们无需创建 GameFactory
即可获得 Game
所以我想知道工厂方法的优点是什么?
该站点的问题过于宽泛,因为它是关于理论而不是要解决的问题。
但简而言之:这是一种模式,您可以随意使用其他模式。
接口的主要目的是让相同方法的不同实现具有不同的 类。因此,工厂提供了以不同方式创建对象的能力,而不是处理更复杂的构造函数。例如,您需要不同的代码来构造具有相同参数的对象,可能基于 OS 或运行代码的应用程序服务器。那么,你会怎么做呢?然后有一种方法可以为 Linux 提供工厂,另一种方法可以为 Windows 提供工厂,依此类推。
你不需要到处都有...
另一个优点是关于内存管理和垃圾收集器。通过在方法内部使用 new ,很可能会以孤立实例结束,该实例只能通过非常昂贵的完整 GC 进行清理。工厂作为单例永远不会发生,
顺便说一句:在您的简单示例中,既不需要接口也不需要工厂。
同样有多种样式可供选择以适应实际需要。
工厂模式的存在是为了提供函数式编程习惯用法的面向对象版本:提供生成值的仿函数。大多数情况下,您会看到 Factory 与诸如 Strategy 模式之类的东西相结合,其中您有一个接口表示一种做某事的方式,以及该接口的多个具体实现,表示不同的方式来做那件事。工厂的价值在于它抽象了策略的创建。
在实践中,我发现这在两种情况下很有用:
当客户端代码需要选择策略,但库代码需要控制何时以及多久使用该策略时。这与您的游戏示例相匹配:GameFactory
代表 种类 游戏(国际象棋、跳棋等),而 Game
实例代表特定游戏。如果你正在编写一个 class 来玩不同玩家之间的比赛锦标赛,你需要用 GameFactory 来构造它来告诉它玩什么样的游戏,但锦标赛本身会决定开始多少场比赛以及如何操纵每场比赛。 Tournament 不能只做 new Chess()
或 new Checkers()
因为目标是让 Tournament class 独立于游戏类型,这样它就可以重复使用。
当创建一个值很复杂并且您基本上希望对构造函数进行部分应用时,工厂也很有用。这种方法通常与依赖注入模式一起使用效果很好。例如,假设您现在添加了第三种游戏 Poker
。 Poker
游戏需要一个随机数生成器,以便它知道如何洗牌。您如何将其融入您现有的 GameFactory/Game/Tournament 结构? PokerFactory
对象会将随机数生成器作为参数,并负责将随机数生成器传递给它创建的任何 PokerGame
对象。使用 GameFactory/Game API 的 class,例如 Tournament,不需要知道有些游戏需要随机数生成器,有些则不需要;这些知识将被抽象掉。
退一步说:像大多数面向对象设计模式一样,工厂模式的全部目的是减少耦合和增加内聚 .您不希望您的锦标赛 class 与任何特定游戏 耦合 ;解耦代码使程序的不同部分可以相互独立地更改。
更进一步:软件工程的大部分知识和最佳实践都来自大型软件项目,代码行太多,没有人能记住所有的代码这种情况无处不在,而且有这么多人参与这个项目,协调项目不同部分之间的工作成为需要解决的最重要的问题之一。之所以存在如此多的设计模式,是因为它们在程序的不同部分之间定义了 "seams" 以划分您需要同时牢记在脑中的概念。这允许两个或多个程序员同时在不同的隔间上工作,并使您更容易确保每个隔间的正确性。
在您学习编程语言或首次学习编程时编写的那种 "toy" 程序中,这些划分程序的策略可能看起来是多余的或只是令人困惑。事实上,如果你不必要地应用这些类型的模式,那么它们实际上会使事情变得更糟,而不是更好。但是,当您处理大量代码时,这些技术 "tools" 是您的工具带:既可以让您更好地理解现有代码是如何划分的,也可以划分您编写的代码。
TL;DR: "Everything should be made as simple as possible, but no simpler."
"Thinking In Java"中有一个Interface and Factories
的例子如下:
(1) 定义游戏和 GameFactory 接口
interface Game { boolean move(); }
interface GameFactory { Game getGame(); }
(2)写出它们的实现
class Checkers implements Game {
private int moves = 0;
private static final int MOVES = 3;
public boolean move() {
System.out.println("Checkers move " + moves);
return ++moves != MOVES;
}
}
class CheckersFactory implements GameFactory {
public Game getGame() { return new Checkers(); }
}
class Chess implements Game {
private int moves = 0;
private static final int MOVES = 4;
public boolean move() {
print("Chess move " + moves);
return ++moves != MOVES;
}
}
class ChessFactory implements GameFactory {
public Game getGame() { return new Chess(); }
}
(3) 和客户端代码
public class Games {
public static void playGame(GameFatory factory) {
Game s = factory.getGame();
while (s.move());
}
public static void main(String[] args) {
playGame(new CheckersFactory());
playGame(new ChessFactory());
}
}
那么对于客户端代码,我们何必把GameFactory
传给playGame
的函数呢?
我们可以直接设置Game
作为参数如下:
public class Games {
public static void playGame(Game game) {
while (game.move());
}
public static void main(String[] args) {
playGame(new Checkers());
playGame(new Chess());
}
}
因此我们无需创建 GameFactory
即可获得 Game
所以我想知道工厂方法的优点是什么?
该站点的问题过于宽泛,因为它是关于理论而不是要解决的问题。
但简而言之:这是一种模式,您可以随意使用其他模式。
接口的主要目的是让相同方法的不同实现具有不同的 类。因此,工厂提供了以不同方式创建对象的能力,而不是处理更复杂的构造函数。例如,您需要不同的代码来构造具有相同参数的对象,可能基于 OS 或运行代码的应用程序服务器。那么,你会怎么做呢?然后有一种方法可以为 Linux 提供工厂,另一种方法可以为 Windows 提供工厂,依此类推。
你不需要到处都有...
另一个优点是关于内存管理和垃圾收集器。通过在方法内部使用 new ,很可能会以孤立实例结束,该实例只能通过非常昂贵的完整 GC 进行清理。工厂作为单例永远不会发生,
顺便说一句:在您的简单示例中,既不需要接口也不需要工厂。
同样有多种样式可供选择以适应实际需要。
工厂模式的存在是为了提供函数式编程习惯用法的面向对象版本:提供生成值的仿函数。大多数情况下,您会看到 Factory 与诸如 Strategy 模式之类的东西相结合,其中您有一个接口表示一种做某事的方式,以及该接口的多个具体实现,表示不同的方式来做那件事。工厂的价值在于它抽象了策略的创建。
在实践中,我发现这在两种情况下很有用:
当客户端代码需要选择策略,但库代码需要控制何时以及多久使用该策略时。这与您的游戏示例相匹配:
GameFactory
代表 种类 游戏(国际象棋、跳棋等),而Game
实例代表特定游戏。如果你正在编写一个 class 来玩不同玩家之间的比赛锦标赛,你需要用 GameFactory 来构造它来告诉它玩什么样的游戏,但锦标赛本身会决定开始多少场比赛以及如何操纵每场比赛。 Tournament 不能只做new Chess()
或new Checkers()
因为目标是让 Tournament class 独立于游戏类型,这样它就可以重复使用。当创建一个值很复杂并且您基本上希望对构造函数进行部分应用时,工厂也很有用。这种方法通常与依赖注入模式一起使用效果很好。例如,假设您现在添加了第三种游戏
Poker
。Poker
游戏需要一个随机数生成器,以便它知道如何洗牌。您如何将其融入您现有的 GameFactory/Game/Tournament 结构?PokerFactory
对象会将随机数生成器作为参数,并负责将随机数生成器传递给它创建的任何PokerGame
对象。使用 GameFactory/Game API 的 class,例如 Tournament,不需要知道有些游戏需要随机数生成器,有些则不需要;这些知识将被抽象掉。
退一步说:像大多数面向对象设计模式一样,工厂模式的全部目的是减少耦合和增加内聚 .您不希望您的锦标赛 class 与任何特定游戏 耦合 ;解耦代码使程序的不同部分可以相互独立地更改。
更进一步:软件工程的大部分知识和最佳实践都来自大型软件项目,代码行太多,没有人能记住所有的代码这种情况无处不在,而且有这么多人参与这个项目,协调项目不同部分之间的工作成为需要解决的最重要的问题之一。之所以存在如此多的设计模式,是因为它们在程序的不同部分之间定义了 "seams" 以划分您需要同时牢记在脑中的概念。这允许两个或多个程序员同时在不同的隔间上工作,并使您更容易确保每个隔间的正确性。
在您学习编程语言或首次学习编程时编写的那种 "toy" 程序中,这些划分程序的策略可能看起来是多余的或只是令人困惑。事实上,如果你不必要地应用这些类型的模式,那么它们实际上会使事情变得更糟,而不是更好。但是,当您处理大量代码时,这些技术 "tools" 是您的工具带:既可以让您更好地理解现有代码是如何划分的,也可以划分您编写的代码。
TL;DR: "Everything should be made as simple as possible, but no simpler."