返回信息流Java里面,构造函数不能作为接口中的抽象方法。
很奇怪吧,对象的行为可以抽象,对象的创造却不能抽象。
举个例子:
interface Animal {
String speak();
}
class Cat extends Animal {
public Cat() {} // 注意,构造函数并没有Override任何东西
@Override public String speak() { return "meow..."; }
}
class Dog extends Animal {
public Dog() {} // 同理
@Override public String speak() { return "woof!"; }
}
所以,你不能说,给我一个类型,我创建一个对象。Java里没有直接的办法。只能把“创建对象”本身做成一个接口(抽象工厂)的抽象方法,再用具体的工厂来重写该方法,来“制造”对象。这样:
interface AnimalFactory {
Animal makeAnimal();
}
class CatFactory extends AnimalFactory {
public @Override Animal makeAnimal() { return new Cat(); }
}
class DogFactory extends AnimalFactory {
public @Override Animal makeAnimal() { return new Dog(); }
}
public class Main {
public static void makeAnimalAndSpeak(AnimalFactory factory) {
Animal animal = factory.makeAnimal(); // 用工厂创建对象
System.out.println(animal.speak()); // 调用抽象接口的方法,不管具体工厂制造出来的是Cat还是Dog。
}
public static void main(String[] args) {
CatFactory cf = new CatFactory();
DogFactory df = new DogFactory();
makeAnimalAndSpeak(cf);
makeAnimalAndSpeak(df);
}
}
感觉怎么样?我感觉啰嗦。问题的根本在于Java里面构造函数不是接口的一部分。
换一个语言试试。Scala里,按照惯例,程序员应该创建“伴随对象”,里面放上工厂方法。
trait Animal {
def speak(): String
}
class Cat() { // 注意这对括号。主构造函数就是Cat()
def speak(): String = "meow..."
}
object Cat { // Cat的伴随对象。是一个单例(singleton)对象。
def apply(): Cat = new Cat() // 工厂方法。
}
class Dog() {
def speak(): String = "woof!"
}
object Dog { // 同理
def apply(): Dog = new Dog() // 工厂方法
}
object Main extends App {
val myCat = Cat() // 实际调用的是Cat.apply(),就是那个伴随对象的apply方法。
val myDog = Dog() // 实际上调用了Dog.apply()。
def makeAnimalAndSpeak(factory: () => Animal): Unit = {
// 注意这个factory参数的类型。它是一个函数类型,函数变量为空,返回一个Animal。
// 很显然,Cat.apply()和Dog.apply()都满足这个类型。
val animal = factory() // 调用传入的factory函数
println(animal.speak())
}
makeAnimalAndSpeak(Cat.apply) // 方法(函数)就是工厂,工厂就是方法(函数)
makeAnimalAndSpeak(Dog.apply)
}
而Scala语言有语法糖。一旦你把一个对象设置成case class,语言会自动帮你创建伴随对象,并根据主构造函数(class那一行后面的第一对圆括号)来创建apply工厂方法。所以,更简练的写法是:
trait Animal {
def speak(): String
}
case class Cat() { // 是case class,自动帮你创建工厂方法:Cat.apply()
def speak(): String = "meow..."
}
case class Dog() { // 同样,case class。
def speak(): String = "woof!"
}
object Main extends App {
val myCat = Cat() // 工厂方法自动生成好了。
val myDog = Dog() // 直接用就行。
def makeAnimalAndSpeak(factory: () => Animal): Unit = {
// 这里不需要改
val animal = factory()
println(animal.speak())
}
makeAnimalAndSpeak(Cat.apply) // 这里也不用改。
makeAnimalAndSpeak(Dog.apply)
}
现在,Java 1.8有了lambda表达式。如果你的接口只有一个抽象方法,那么就可以用lambda表达式。
interface AnimalFactory { // 这个接口只有一个抽象方法makeAnimal,所以可以用lambda
Animal makeAnimal();
}
public class Main {
public static void makeAnimalAndSpeak(AnimalFactory factory) {
Animal animal = factory.makeAnimal(); // 这里不用变
System.out.println(animal.speak());
}
public static void main(String[] args) {
makeAnimalAndSpeak(() -> new Cat()); // lambda表达式。
makeAnimalAndSpeak(() -> new Dog()); // 这两行直接原地创建了两个工厂
/* 上述写法实际相当于:(以Cat为例)
makeAnimalAndSpeak(new AnimalFactory() {
@Override public Animal makeAnimal() {
return new Cat();
}
});
但是那个lambda表达式() -> new Cat()包含了整个匿名内部类的信息。
*/
}
}
再看看别的语言。Python:
class Cat:
def speak(self):
return "meow..."
class Dog:
def speak(self):
return "woof!"
def make_animal_and_speak(factory):
animal = factory() # 不管传进来的factory是什么,直接“调用”它。实际相当于调用factory.__call__()方法。
animal.speak()
make_animal_and_speak(Cat) # Cat是一个对象,类型是type。Cat()创建一个新的Cat实例
make_animal_and_speak(Dog) # Dog同理
Python里,对象的构造方法实际上就是它的类型类(就是Cat和Dog这两个对象本身)的__call__方法。(注意和__init__不同。这个构造方法过程中会调用__init__,但还包含分配内存等其他事情)所以,类型类本身就是工厂了,不需要额外的工厂类。
总结一下:抽象工厂模式反映了某些语言(如Java和C++)无法将构造方法抽象出来的弱点。
这是一条镜像帖。来源:北邮人论坛 / java / #37093同步于 2014/12/15
该镜像源已超过 30 天没有更新,可能在源站已被删除。
Java机器人发帖
[黑设计模式系列]03:Abstract Factory:抽象工厂模式
nuanyangyang
2014/12/15镜像同步22 回复
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
补充一下。抽象工厂接口还有一个用途:用一个接口来生成一系列的对象。比如:
// 没有真的数据库接口会这样做的,只是举个例子。
interface SqlObjectMaker {
Connection makeConnection();
Statement makeStatement();
ResultSet makeResultSet();
}
class MariaDBSqlObjectMaker implements SqlObjectMaker {
...
}
class OracleSqlObjectMaker implements SqlObjectMaker {
...
}
这样的使用方式倒是可以给使用抽象工厂模式提供依据。但是,一些编程语言里,package或者module也是对象。比如Python:
# mariadb.py
def make_connection():
return MariaDBConnection()
def make_statement():
return MariaDBStatement()
def make_result_set():
return MariaDBCResultSet()
# oracle.py
def make_connection():
return OracleConnection()
def make_statement():
return OracleStatement()
def make_result_set():
return OracleResultSet()
# main.py
import mariadb # mariadb和oracle都是module,import以后,都可以当普通对象使用。
import oracle
def use_db(db_module):
conn = db_module.make_connection() # 调用module里的顶层make_connection函数
stmt = db_module.make_statement() # 同理。也是直接调用顶层函数
rs = db_module.make_result_set() # 同理。
use_db(mariadb) # 直接把module传入
use_db(oracle) # 同理