读书笔记----软件设计原则、设计模式
| 这个作业属于哪个课程 | https://edu.cnblogs.com/campus/gdgy/2021Softwarecodedevelopmenttechnology/ |
|---|---|
| 这个作业要求在哪里 | https://edu.cnblogs.com/campus/gdgy/2021Softwarecodedevelopmenttechnology/homework/11833 |
| 这个作业的目标 | 理解,熟悉设计原则,设计模式 |
参考资料:深入设计模式
工厂模式
1,工厂模式的定义
Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.”(在基类中定义创建对象的一个接口,让子类决定实例化哪个类。工厂方法让一个类的实例化延迟到子类中进行。)
2,工厂模式的分类
(1)简单工厂(Simple Factory)模式,又称静态工厂方法模式(Static Factory Method Pattern)。
(2)工厂方法(Factory Method)模式,又称多态性工厂(Polymorphic Factory)模式或虚拟构造子(Virtual Constructor)模式;
(3)抽象工厂(Abstract Factory)模式,又称工具箱(Kit 或Toolkit)模式。
3,为什么要用工厂模式
(1) 解耦 :把对象的创建和使用的过程分开
(2)降低代码重复: 如果创建某个对象的过程都很复杂,需要一定的代码量,而且很多地方都要用到,那么就会有很多的重复代码。
(3) 降低维护成本 :由于创建过程都由工厂统一管理,所以发生业务逻辑变化,不需要找到所有需要创建对象B的地方去逐个修正,只需要在工厂里修改即可,降低维护成本。
4,简单工厂模式
具体运用
简单工厂模式严格意义上不算是设计模式中的一种,它只是工厂模式的特殊实现,如:
public class ShapeFactory {
// 使用 getShape 方法获取形状类型的对象
public static Shape getShape(String shapeType) {
if (shapeType == null) {
return null;
}
if (shapeType.equalsIgnoreCase("CIRCLE")) {
return new Circle();
} else if (shapeType.equalsIgnoreCase("RECTANGLE")) {
return new Rectangle();
} else if (shapeType.equalsIgnoreCase("SQUARE")) {
return new Square();
}
return null;
}
}
以上的Circle,Rectangle,Square都实现了Shape接口。
但是上面的工厂方法,如果我们要添加其他的Shape 如三角形之类的,就必须修改工厂类的代码,这明显违反了开闭原则,所以我们可以使用反射的方法来优化它
public class ShapeFactory2 {
public static Object getClass(Class<? extends Shape> clazz) {
Object obj = null;
try {
obj = Class.forName(clazz.getName()).newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return obj;
}
}
这种方式的虽然符合了 开放-关闭原则 ,但是每一次传入的都是产品类的全部路径,这样比较麻烦。如果需要改善的话可以通过 反射+配置文件 的形式来改善,这种方式使用的也是比较多的。
优点
- 工厂类含有必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的责任,而仅仅“消费”产品;简单工厂模式通过这种做法实现了对责任的分割,它提供了专门的工厂类用于创建对象。
- 客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以减少使用者的记忆量。
- 通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
缺点
- 由于工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。
- 使用简单工厂模式将会增加系统中类的个数,在一定程序上增加了系统的复杂度和理解难度。
- 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。
- 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。
使用场景
- 工厂类负责创建的对象比较少:由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。
- 客户端只知道传入工厂类的参数,对于如何创建对象不关心:客户端既不需要关心创建细节,甚至连类名都不需要记住,只需要知道类型所对应的参数。
5,工厂方法模式
工厂方法模式应该是在工厂模式家族中是用的最多模式,一般项目中存在最多的就是这个模式。
工厂方法模式是简单工厂的仅一步深化, 在工厂方法模式中,我们不再提供一个统一的工厂类来创建所有的对象,而是针对不同的对象提供不同的工厂。也就是说 每个对象都有一个与之对应的工厂 。
具体运用
工厂方法模式需要一个抽象工厂,还有一个具体实现了抽象工厂接口的具体的工厂
//抽象工厂
public interface Factory {
public Shape getShape();
}
//圆形工厂
public class CircleFactory implements Factory {
@Override
public Shape getShape() {
return new Circle();
}
}
//矩形工厂
public class RectangleFactory implements Factory{
@Override
public Shape getShape() {
return new Rectangle();
}
}
//正方形工厂
public class SquareFactory implements Factory{
@Override
public Shape getShape() {
return new Square();
}
}
上面的Circle,Rectangle,Square都是Shape的子类
优点
- 隐藏了具体的实例过程,用户不用知道实例是如何创建的,甚至不需要知道类名,只需要知道从这个工厂可以获得该产品就可以了
- 工厂都是继承同一个抽象的父类工厂,使得创建什么产品是由工厂决定的,而产品创建的细节又封装在具体工厂内部,因为所有具体工厂类都有同一个抽象父类,所有工厂方法又被称作多态工厂模式
- 当要添加产品的时候无需修改现有的所有东西,只需要创建一个新的产品类和具体的工厂就好了,扩展性良好,符合“开闭原则”
缺点
- 在添加新产品时,需要提供与之对应的具体工厂类,使得类的数量大量增加,提高了系统的复杂度,更多的类需要编译运行,给系统带来了额外的开销
- 由于考虑到系统的可扩展性,需要引入抽象层,增加了系统的抽象性和理解难度
适用场景
- 一个类不知道它所需要的对象的类:在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建;客户端需要知道创建具体产品的工厂类。
- 一个类通过其子类来指定创建哪个对象:在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。
- 将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无需关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。
6,抽象工厂模式
在工厂方法模式中,其实我们有一个潜在意识的意识。那就是我们生产的都是同一类产品。抽象工厂模式是工厂方法的仅一步深化,在这个模式中的工厂类不单单可以创建一种产品,而是可以创建一组产品。
具体运用
抽象工厂方法是为了,当两种及两种产品有在逻辑上的关系时,可以把他们当作一个产品族来生产,也就是同一个系列的产品都由同一个工厂来生产
如:不同枪与其对应的子弹一般都是一一对应的,所以生产一把枪和它的子弹的工厂可以是同一个,将它们作为一个产品族来生产。
//枪的接口
public interface Gun {
public void shooting();
}
//子弹接口
public interface Bullet {
public void load();
}
//一种枪
public class AK implements Gun{
@Override
public void shooting() {
System.out.println("shooting with AK");
}
}
//另一种枪
public class M4A1 implements Gun {
@Override
public void shooting() {
System.out.println("shooting with M4A1");
}
}
//枪的子弹
public class AK_Bullet implements Bullet {
@Override
public void load() {
System.out.println("Load bullets with AK");
}
}
//另一种枪的子弹
public class M4A1_Bullet implements Bullet {
@Override
public void load() {
System.out.println("Load bullets with M4A1");
}
}
下面是工厂类的创建:
public interface Factory {
public Gun produceGun();
public Bullet produceBullet();
}
public class AK_Factory implements Factory{
@Override
public Gun produceGun() {
return new AK();
}
@Override
public Bullet produceBullet() {
return new AK_Bullet();
}
}
public class M4A1_Factory implements Factory{
@Override
public Gun produceGun() {
return new M4A1();
}
@Override
public Bullet produceBullet() {
return new M4A1_Bullet();
}
}
优点
- 抽象工厂模式隔离了具体类的生成,使得客户不需要知道什么被创建了,更换一个具体工厂变得相对容易,抽象工厂中定义了公共接口,只需要改变具体的工厂实例就能改变系统的行为,同时抽象工厂模式可以实现高内聚低耦合的设计目的,
- 通过同一个工厂来生产同一个产品族的产品,能够保障了客户端只使用同一产品族中的对象
- 增加具体的工厂和产品族很方便,无需修改已有的系统,符合开闭原则
缺点
- 添加新产品时,难以扩展抽象工厂来生产新种类的产品,因为在抽象的工厂角色中规定了所有可能被创建的产品集合,要添加新的产品种类的时候,需要对抽象工厂的接口和具体工厂的方法的修改,带来很大的不方便
- 开闭原则的倾斜性(增加新的工厂和产品族容易,增加新的产品等级结构麻烦)
开闭原则”的倾斜性
-
“开闭原则”要求系统对扩展开放,对修改封闭,通过扩展达到增强其功能的目的。对于涉及到多个产品族与多个产品等级结构的系统,其功能增强包括两方面:
-
增加产品族:对于增加新的产品族,工厂方法模式很好的支持了“开闭原则”,对于新增加的产品族,只需要对应增加一个新的具体工厂即可,对已有代码无须做任何修改。
-
增加新的产品等级结构:对于增加新的产品等级结构,需要修改所有的工厂角色,包括抽象工厂类,在所有的工厂类中都需要增加生产新产品的方法,不能很好地支持“开闭原则”。
-
-
抽象工厂模式的这种性质称为“开闭原则”的倾斜性,抽象工厂模式以一种倾斜的方式支持增加新的产品,它为新产品族的增加提供方便,但不能为新的产品等级结构的增加提供这样的方便。
适用场景
- 和工厂方法一样客户端不需要知道它所创建的对象的类。
- 需要一组对象共同完成某种功能时,并且可能存在多组对象完成不同功能的情况。(同属于同一个产品族的产品)
- 属于同一个产品族的产品将一起使用,这一约束必须在系统的设计中体现出来
- 系统结构稳定,不会频繁的增加对象。(因为一旦增加就需要修改原有代码,不符合开闭原则)
浙公网安备 33010602011771号