一个类里使用其他类,最常用的方式是 new
一个对象,这属于一种硬编码
。每new 一个对象,增加了类与类之间的联系,不利于程序的松耦
合。
简单工厂模式
public class FruitFactory {
public Fruit create(String type){
switch (type){
case "苹果":
AppleSeed appleSeed = new AppleSeed();
Sunlight sunlight = new Sunlight();
Water water = new Water();
return new Apple(appleSeed, sunlight, water);
case "梨子": return new Pear();
default: throw new IllegalArgumentException("暂时没有这种水果");
}
}
}
public class User {
private void eat(){
FruitFactory fruitFactory = new FruitFactory();
Fruit apple = fruitFactory.create("苹果");
Fruit pear = fruitFactory.create("梨子");
apple.eat();
pear.eat();
}
}
好处
1、将构建过程封装的好处不仅可以 降低耦合,
2、如果某个产品构造方法相当复杂,使用工厂模式可以大大减少代码重复
弊端:
1、一是如果需要生产的产品过多,此模式会导致 工厂类过于庞大,承担过多的职责,变成超级类。当苹果生产过程需要修改时,要来修改此工厂。梨子生产过程需要修改时,也要来修改此工厂。也就是说这个类不止一个引起修改的原因。违背了单一职责原则。
2、二是当要生产新的产品时,必须在工厂类中添加新的分支。而开闭原则告诉我们:类应该对修改封闭。我们希望在添加新功能时,只需增加新的类,而不是修改既有的类,所以这就违背了开闭原则。
工厂方法模式
Gof原文:
Define an interface for creating an object,but let subclasses decide which class to instantiate.Factory Method lets a class defer instantiation to subclasses.
为了解决简单工厂模式的这两个弊端
public interface IFactory {
# 不同方法 类比 产品等级结构
Fruit create();
}
# 实现类 类比 产品族
public class AppleFactory implements IFactory {
@Override
public Fruit create(){
return new Apple();
}
}
public class PearFactory implements IFactory {
@Override
public Fruit create(){
return new Pear();
}
}
public class User {
private void eat(){
IFactory appleFactory = new AppleFactory();
Fruit apple = appleFactory.create();
IFactory pearFactory = new PearFactory();
Fruit pear = pearFactory.create();
apple.eat();
pear.eat();
}
}
# 接口制定规则,规定所有Model都要有build方法
public interface ITeacher {
void build();
}
# 定义Model
public class ChineseTeacher implements ITeacher {
@Override
public void build() {
System.out.println("Build ChineseTeacher");
}
}
public class MathTeacher implements ITeacher {
@Override
public void build() {
System.out.println("Build MathTeacher");
}
}
# 抽象类是抽取所有工厂都具有的能力,为了不重复提取出抽象方法
# create所有方法都有,createTeacher交给工厂灵活实现
public abstract class TeacherFactory {
public ITeacher create() {
ITeacher teacher = createTeacher();
teacher.build();
return teacher;
}
protected abstract ITeacher createTeacher();
}
public class ChineseTeacherFactory extends TeacherFactory {
@Override
protected ITeacher createTeacher() {
return new ChineseTeacher();
}
}
public class MathTeacherFactory extends TeacherFactory {
@Override
protected ITeacher createTeacher() {
return new MathTeacher();
}
}
# 使用
TeacherFactory chineseTeacherFactory = new ChineseTeacherFactory();
ChineseTeacher iTeacher = (ChineseTeacher) chineseTeacherFactory.create();
System.out.println(iTeacher.getClass());
TeacherFactory mathTeacherFactory = new MathTeacherFactory();
MathTeacher iTeacher1 = (MathTeacher) mathTeacherFactory.create();
System.out.println(iTeacher1.getClass());
工厂方法模式解决了简单工厂模式的两个弊端。
1、 当生产的产品种类越来越多时,工厂类不会变成超级类。工厂类会越来越多,保持灵活。不会越来越大、变得臃肿。如果苹果的生产过程需要修改时,只需修改苹果工厂。梨子的生产过程需要修改时,只需修改梨子工厂。符合单一职责原则。
2、当需要生产新的产品时,无需更改既有的工厂,只需要添加新的工厂即可。保持了面向对象的可扩展性,符合开闭原则。
工厂方法模式在Logback源码中的应用
Logback中已经分离出不同工厂负责创建不同日志框架,如Substitute日志框架、NOP日志框架、Log4J日志框架
抽象工厂模式
Gof原文
Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
产品等级结构(Product Hierarchy)
和产品族(Product Family)
是设计模式中的概念,特别是抽象工厂模式(Abstract Factory Pattern)中使用的重要术语。
# 接口制定规则,规定所有Model都要有build方法
public interface ITeacher {
void build();
}
public interface ITutor {
void build();
}
# 定义Model
public class ChineseTeacher implements ITeacher {
@Override
public void build() {
System.out.println("Build ChineseTeacher");
}
}
public class MathTeacher implements ITeacher {
@Override
public void build() {
System.out.println("Build MathTeacher");
}
}
public class ChineseTutor implements ITutor {
@Override
public void build() {
System.out.println("Build ChineseTutor");
}
}
public class MathTutor implements ITutor {
@Override
public void build() {
System.out.println("Build MathTutor");
}
}
# 抽象工厂(多个工厂,所以抽象出一个接口约定行为)
public interface IGroup {
ITutor createTutor();
ITeacher createTeacher();
}
public class ChineseIGroup implements IGroup {
@Override
public ITutor createTutor() {
ChineseTutor chineseTutor = new ChineseTutor();
chineseTutor.build();
return chineseTutor;
}
@Override
public ITeacher createTeacher() {
ChineseTeacher chineseTeacher = new ChineseTeacher();
chineseTeacher.build();
return chineseTeacher;
}
}
public class MathIGroup implements IGroup {
@Override
public ITutor createTutor() {
MathTutor mathTutor = new MathTutor();
mathTutor.build();
return mathTutor;
}
@Override
public ITeacher createTeacher() {
MathTeacher mathTeacher = new MathTeacher();
mathTeacher.build();
return mathTeacher;
}
}
# 使用
IGroup chineseGroup = new ChineseIGroup();
ChineseTeacher chineseTeacher = (ChineseTeacher) chineseGroup.createTeacher();
System.out.println(chineseTeacher.getClass());
ChineseTutor chineseTutor = (ChineseTutor) chineseGroup.createTutor();
System.out.println(chineseTutor.getClass());
IGroup mathGroup = new MathIGroup();
MathTeacher mathTeacher = (MathTeacher) mathGroup.createTeacher();
System.out.println(mathTeacher.getClass());
MathTutor mathTutor = (MathTutor) mathGroup.createTutor();
System.out.println(mathTutor.getClass());
我们在创建时指定了具体的工厂类后,在使用时就无需再关心是哪个工厂类
抽象工厂模式很好的发挥了开闭原则、依赖倒置原则,但缺点是 抽象工厂模式太重了,如果 IFactory 接口需要新增功能,则会影响到所有的具体工厂类。使用抽象工厂模式,替换具体工厂时只需更改一行代码
,但要新增抽象方法则需要修改所有的
具体工厂类。所以抽象工厂模式适用于增加同类工厂这样的横向扩展需求
,不适合新增功能这样的纵向扩展
。
抽象工厂在实际中的使用例子
抽象工厂模式 主要 用于替换一系列方法
例如将程序中的 SQL Server 数据库整个替换为 Access 数据库,使用抽象方法模式的话,只需在 IFactory 接口中定义好增删改查四个方法
,让 SQLFactory 和 AccessFactory 实现此接口
,调用时直接使用 IFactory 中的抽象方法即可
,调用者无需知道使用的什么数据库,我们就可以非常方便的整个替换程序的数据库,并且让客户端毫不知情。
[Q&A] 工厂方法模式 和 抽象工厂模式的区别
工厂方法关注单个产品等级结构(Product Hierarchy)
的实例化,强调的是单一对象的创建过程。
抽象工厂则关注多个产品族(Product Family)
的实例化,强调的是多个相互关联的产品对象的协调创建过程。
在实际运用中,如果需求是创建某一类对象,并且这个对象类型可能会扩展,则适合使用工厂方法模式;
如果需要创建一系列相关的对象,而这些对象间又具有某种一致性约束或联系,那么抽象工厂模式更为合适。