如何创建一个java 对象? new呗
new 有什么问题?
- 实例化总是公开的进行;
- 初始化经常造成“耦合”;
设计原则不是说?“针对接口编程,不针对实现编程吗?”,new 创建对象不是在针对实现编程吗?
是的,一旦加入新的类(变化),就需要重新修改代码,使得代码变得越来越难以维护。
如何处理?
第一步:找出变化的部分。(设计原则一,封装变化)
将根据不同条件创建不同对象的代码封装进入一个类中,该类专职创建实例对象。这个类就叫做工厂。 工厂处理创建对象的细节。
静态工厂?
利用静态方法定义的工厂成为静态工厂。使用静态工厂不需要使用实例对象的方法创建对象。但是,静态方法不能通过继承来改变对象的创建行为。
简单工厂
简单工厂不是一个设计模式,而是一种编程习惯。但由于经常被使用,经常被误认为是一种设计模式。
public class SimplePizzaFactory {
public Pizza createPizza (String type) {
Pizza pizza = null;
if ("cheese".equals(type)) {
pizza = new CheesePizza();
}else if ("greek".equals(type)) {
pizza = new GreekPizza();
}else if ("perpperoni".equals(type)) {
pizza = new PepperoniPizza();
}
return pizza;
}
}
工厂方法模式
定义:
定义一个创建对象的接口, 但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
所有工厂模式都用来封装对象的创建。
工厂方法用来处理对象的创建, 并将这样的行为封装在子类中。这样,客户程序中关于超类的代码就和子类对象创建代码解耦了。
-
创建者(如下图PizzaStore)
创建者定义了抽象的工厂方法,让子类实现此方法制造产品(如createPizza()方法)
创建者通常会包含依赖于抽象产品的代码,而这些抽象产品由子类制造,创建者不需要知道在哪制造哪种具体产品
创建者的子类称为具体创建者(如下图NJPizzaStore)。 -
产品类 (如下图Pizza)
具体创建者执行制造产品的方法,返回的类型即为产品类。产品类的子类为具体的产品(如SugarPizza或CheesePizza)。
理解“决定”?
由子类“决定”要实例化的类是哪一个。不是指在运行时做决定,而是指在编写创建者类时,不需要知道实际创建的产品是哪一个。选择使用哪个子类,自然就决定了实际创建的产品是什么。
一定要使用参数创建对象吗?
不是的。“参数化工厂方法”可以根据传入的参数创建不同的对象。实际上,工厂经常只产生一种对象,不需要参数化。
工厂方法与简单工厂的差异?
工厂方法的实际创建者与简单工厂很相似, 简单工厂在一个地方把全部的事情都处理完,然而工厂方法确实创建一个框架,让子类决定如何实现。
二者都可以将对象的创建封装起来,但是工厂方法更具有弹性,因为工厂方法通过使用不同的具体创建者可以变更创建的产品。
“工厂”究竟有什么好处??
-
- 封装创建对象的代码,避免代码重复,方便维护
-
- 实例化对象时依赖于接口(抽象类或接口),而不是具体类。
-
- 让代码更具有弹性,应对“变化”
依赖倒置原则
要依赖抽象,不要依赖具体类。
该原则类似于“针对接口编程,不针对实现编程”,但更强调“抽象”。这个原则说明了:不能让高层组件依赖低层组件,而且,不管高层或低层组件,“两者”都应该依赖于抽象。
依赖倒置原则,倒置在哪里?
倒置指的是和一般的OO设计的思考方式完全相反。在上图中,高层类PizzaStore 依赖Pizza这一抽象类, 低层的CheesePizza和SugarPizza也依赖于Pizza这一抽象类。就像一个沙漏的结构,而完全的自上而下的依赖结构则像是一个金字塔结构。
如何在实际工作中避免违反依赖倒置原则??
- 变量不可以持有具体类的引用 如果使用new,就会持有具体类的引用,采用工厂来避免这样的做法
- 不要让类派生自具体类 派生具体类,就会依赖具体类,请派生一个抽象(接口或抽象类)
- 不要覆盖基类中已实现的方法。
如果覆盖基类已实现的方法,那么基类就不是一个真正适合被继承的抽象。基类中实现的方法,应该由所有的类共享。
实际工作中想要完全遵循以上三个方针很难,而且很多时候完全没必要。
抽象工厂模式
定义: 提供一个接口, 用于创建相关对象的家族或依赖对象的家族,而不需要明确指定具体类。
抽象工厂允许客户使用抽象的接口创建一组相关的产品,而不需要关心实际产出的具体类是什么。这样一来,客户从具体的产品中被解耦。
PizzaIngredientFactory 为原料工厂接口, NJPizzaIngredientFactory为一个具体的原料工厂,它生产特定的Dough、Souce、Cheese
抽象类PizzaStore(披萨店)定义了 createPizza()方法,子类NJPizzaStore(南京披萨店)实现创建Pizza的方法,在准备过程中prepare(),从指定的原料工厂NJPizzaIngredientFactory 获取原料。
在上述过程中,Dough、Souce、Cheeses是一个产品家族,每个原料工厂生产不同的原料。
发现: 抽象工厂的每个方法经常已工厂方法的方式实现。抽象工厂的任务是定义一个负责创建一组产品的接口。接口中得每个方法都负责创建一个具体产品,具体的产品做法由实现抽象工厂的子类提供。
工厂方法与抽象工厂之间的异同??它们之间有什么关联关系??
工厂方法: 使用类, 采用继承的方法,通过子类创建对象。客户只需知道所使用的抽象类,子类负责决定具体类型。只需要一个方法就行。
抽象工厂: 使用对象,采用组合的方法,定义一个产品家族被产生的方法。使用抽象工厂必须先实例化,再将其传入一些针对抽象类型的业务逻辑中。 抽象工厂在扩展产品时则必须改变接口,接口的所有实现类也就必须改变,是很繁重的工作。
相同点: 都能将对象的创建封装起来,使应用程序解耦,并降低对具体实现的依赖。
OO原则总结
- 封装变化
- 针对接口编程,不针对实现编程
- 多用组合,少用继承
- 为交互对象之间的松耦合设计而努力
- 类应该对扩展开发,对修改关闭
- 依赖抽象,不依赖具体类(依赖倒置原则)
要点总结
- 所有的工厂都是用来封装对象的创建
- 简单工厂,虽然不是真正的设计模式,但却是跟具体类之间解耦的简单方法
- 工厂方法使用继承:把对象的创建交给子类,子类实现工厂方法来创建对象
- 抽象工厂使用对象组合:对象的创建被实现在工厂接口暴露的方法中。
- 所有工厂模式都通过减少应用程序与具体类之间的依赖促进松耦合。
- 工厂方法允许类将实例化延迟到子类中进行
- 抽象工厂创建相关的对象家族,而不须依赖它们的具体类
- 依赖倒置原则:避免依赖具体类,尽量依赖抽象。
- 工厂模式帮助我们针对抽象编程,而不针对具体类编程。