一、单例模式
采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例, 并且该类只提供一个取得其对象实例的方法(静态方法)。
单例模式分为 八种、
饿汉:先给我 ,就是什么都创建好 等着被调用 可能造成内存浪费
懒汉:饿了在给我 就是:什么时候用 什么时候创建
- 饿汉式(静态常量)
- 饿汉式(静态代码块)
- 懒汉式(线程不安全)
- 懒汉式(线程安全,同步方法 效率低)
- 双重检查 (volatile synchronized) 建议使用
- 静态内部类 建议使用
- 枚举 建议使用
单例模式注意事项和细节说明
- 单例模式保证了 系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使 用单例模式可以提高系统性能
- 当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用 new
单例模式使用的场景:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即:重量级 对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session 工厂等)
1.1、饿汉式静态变量
实现步骤:
- 构造器私有化 (防止 new )
- 本类的内部创建对象实例
- 提供一个公有的静态方法,返回实例对象
-
优点:写法简单、在启动项目,类装载时 就完成了其实例化对象,避免线程同步的问题
-
缺点:如果这个类对象用不到,但还是实例化了,造成了内存浪费
-
结论:这种单例模式 可用,但 可能造成内存浪费,如果 这种单例太多,启动时一次性加载,啧啧啧,可怕的
public class A_SingletonPattern {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
// 证明对象是同个对象
System.out.println(instance == instance1); // true
System.out.println("instance hashCode = " + instance.hashCode()); // instance hashCode = 789451787
System.out.println("instance1 hashCode = " + instance.hashCode()); // instance1 hashCode = 789451787
}
}
// 饿汉式(静态常量)
class Singleton{
// 1、私有化 构造器
private Singleton(){
}
// 2、类的内部创建对象实例
private final static Singleton instance = new Singleton();
// 3、提供一个公有的静态方法,返回实例对象
public static Singleton getInstance(){
return instance;
}
}
解读代码:
对 Singleton类
1、私有化构造器,防止new它
// 1、私有化 构造器
private Singleton(){
}
2、类的内部创建对象实例 静态的且是常量 在启动项目是 就创建了该对象
private final static Singleton instance = new Singleton();
3、提供一个公有的静态方法,返回实例对象
public static Singleton getInstance(){
return instance;
}
main 方法 通过== 和 对象的hashcode 验证是同一个 Singleton 对象
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
// 证明对象是同个对象
System.out.println(instance == instance1); // true
System.out.println("instance hashCode = " + instance.hashCode()); // instance hashCode = 789451787
System.out.println("instance1 hashCode = " + instance.hashCode()); // instance1 hashCode = 789451787
}
1.2、饿汉式 静态代码块
class SingletonB {
// 1、私有化 构造器
private SingletonB() {
}
// 2、类的内部创建对象实例
private static SingletonB instance;
// 在静态代码块中,创建单例对象
static {
instance = new SingletonB();
}
// 3、提供一个公有的静态方法,返回实例对象
public static SingletonB getInstance() {
return instance;
}
}
代码解读:
静态常量类似,只不过将类实例化的过程放在了静态代码块中,也是在类装载的时候,就执 行静态代码块中的代码,初始化类的实例。
// 2、类的内部创建对象实例
private static SingletonB instance;
// 在静态代码块中,创建单例对象
static {
instance = new SingletonB();
}
1.3、懒汉式(单线程)
线程不安全 只能在单线程下使用
优缺点:
- 起到了 懒加载(Lazy Loading) 的效果,调用的时候再创建对象实例 但只能单线程下使用
- 如果在多线程下 ,一个线程第一个走到 if (instance == null) 这是需要创建一个对象,
但是还没来得及往下走,另一个线程也走到这 也要去创建一个对象,这就会产生多个实例
结论:不要使用该方式 除非 上锁 synchronized 效率慢
public static void main(String[] args) {
SingletonC instance = SingletonC.getInstance();
SingletonC instance1 = SingletonC.getInstance();
// 证明对象是同个对象
System.out.println(instance == instance1); // true
System.out.println("instance hashCode = " + instance.hashCode()); // instance hashCode = 789451787
System.out.println("instance1 hashCode = " + instance.hashCode()); // instance1 hashCode = 789451787
}
}
class SingletonC{
// 1、私有化 构造器
private SingletonC(){
}
private static SingletonC instance;
// 3、提供一个公有的静态方法,调用的时候 再创建该对象的实例
public static SingletonC getInstance(){
if (instance == null){
instance = new SingletonC();
}
return instance;
}
}
1.4、 懒汉式(线程安全)
优缺点:
- 解决线程安全问题
- 效率太低了 ,该方法每次只能有一个线程进入,效率降低
结论:不推荐使用这种方式
public static void main(String[] args) {
SingletonD instance = SingletonD.getInstance();
SingletonD instance1 = SingletonD.getInstance();
// 证明对象是同个对象
System.out.println(instance == instance1); // true
System.out.println("instance hashCode = " + instance.hashCode()); // instance hashCode = 789451787
System.out.println("instance1 hashCode = " + instance.hashCode()); // instance1 hashCode = 789451787
}
}
class SingletonD{
// 1、私有化 构造器
private SingletonD(){
}
private static SingletonD instance;
// 3、提供一个公有的静态方法,加入synchronized 解决线程安全问题 效率 低
public static synchronized SingletonD getInstance(){
if (instance == null){
instance = new SingletonD();
}
return instance;
}
}
使用 synchronized 保证线程安全
1.5、双重检查
-
优点: 线程安全 延迟加载 效率较高
-
结论:推荐使用
上代码:
public static void main(String[] args) {
SingletonE instance = SingletonE.getInstance();
SingletonE instance1 = SingletonE.getInstance();
// 证明对象是同个对象
System.out.println(instance == instance1); // true
System.out.println("instance hashCode = " + instance.hashCode()); // instance hashCode = 789451787
System.out.println("instance1 hashCode = " + instance.hashCode()); // instance1 hashCode = 789451787
}
class SingletonE{
// 1、私有化 构造器
private SingletonE(){}
private static volatile SingletonE instance;
// 3、提供一个公有的静态方法,加入synchronized 同步代码块
public static SingletonE getInstance(){
if (instance == null){
synchronized (SingletonE.class){
if (instance == null){
instance = new SingletonE();
}
}
}
return instance;
}
}
解读:
1、首先 私有化构造器 ,避免 外部 用new 创建此对象
// 1、私有化 构造器
private SingletonE(){}
2、其次 本类的对象置为全局参数,并 有 volatile 修饰
private static volatile SingletonE instance;
volatile:上个线程的操作对 后一个线程可见,保证 new 的时候不会发生指令重排
3、最后:
多线程情况下,一个线程 走进 synchronized 代码块中 判断为null 创建对象,
另一个线程 在同步代码块之外等待,因为volatile,上个线程 创建对象对该线程是可见的
所以 他的instance == null 不成立 直接返回 上个线程创建的对象。
双重检查是 两次判断本类对象是否为空
// 3、提供一个公有的静态方法,加入synchronized 同步代码块
public static SingletonE getInstance(){
if (instance == null){
synchronized (SingletonE.class){
if (instance == null){
instance = new SingletonE();
}
}
}
return instance;
}
1.6、静态内部类
优缺点说明:
- 这种方式采用了类装载的机制来保证初始化实例时只有一个线程。
- 静态内部类方式在 Singleton 类被装载时并不会立即实例化,而是在需要实例化时,调用 getInstance 方法,才 会装载 SingletonInstance 类,从而完成 Singleton 的实例化。
- 类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM 帮助我们保证了线程的安全性,在类进行 初始化时,别的线程是无法进入的。
- 优点:避免了线程不安全,利用静态内部类特点实现延迟加载,效率高
结论:推荐使用.
public static void main(String[] args) {
SingletonF instance = SingletonF.getInstance();
SingletonF instance1 = SingletonF.getInstance();
// 证明对象是同个对象
System.out.println(instance == instance1); // true
System.out.println("instance hashCode = " + instance.hashCode()); // instance hashCode = 789451787
System.out.println("instance1 hashCode = " + instance.hashCode()); // instance1 hashCode = 789451787
}
}
class SingletonF{
// 1、私有化 构造器
private SingletonF(){}
// 2、写一个静态内部类,该类中有静态属性
private static class SingletonInstance{
private static final SingletonF SINGLETON =new SingletonF();
}
// 3、提供一个静态公有方法,直接返回 SingletonInstance.SINGLETON
public static SingletonF getInstance(){
return SingletonInstance.SINGLETON;
}
}
1.7、枚举方式
优缺点说明:
- 这借助 JDK1.5 中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建 新的对象。
- 这种方式是 Effective Java 作者 Josh Bloch 提倡的方式
结论:推荐使用
public static void main(String[] args) {
SingletonG instance = SingletonG.INSTANCE;
SingletonG instance1 = SingletonG.INSTANCE;
// 证明对象是同个对象
System.out.println(instance == instance1); // true
System.out.println("instance hashCode = " + instance.hashCode()); // instance hashCode = 789451787
System.out.println("instance1 hashCode = " + instance.hashCode()); // instance1 hashCode = 789451787
instance.sayOk();
}
}
// 使用枚举方式 实现单例模式
enum SingletonG{
INSTANCE;
public void sayOk(){
System.out.println("ok");
}
}
单例模式在JDK的应用
java.lang.Runtime 就是经典的单例模式(饿汉式)
二、工厂模式
分为 三类:
1、简单工厂模式
工厂类为一个类。根据不同类型生成不同产品
2、工厂方法模式
工厂类为 抽象类 , 有不同的子类(子工厂)继承他,并重写 生成产品的方法,满足不同子工厂生成不同产品需求
3、抽象工厂模式
工厂类为接口 有不同的子类(子工厂)继承他,并重写 生成产品的方法,满足不同子工厂生成不同产品需求
2.1、 简单工厂模式
简单工厂模式:
- 介绍:属于 创建型模式,工厂模式的一种。简单工厂模式 是由一个工厂对象决定创建除哪一种产品的实例,是工厂模式中最简单实用的模式
- 实现:定义一个创建对象的类,有这个类封装实例化对象的行为
- 使用:在 软件开发中,当我们会用到大量的创建某种、某类或者某批对象时,会用到工厂模式
通过案例来解释 简单工厂模式
需求:要便于披萨种类的扩展,便于维护
- 1、披萨的种类很多(GreekPizza、CheesePizza…)
- 2、披萨的制作有 prepare(预备)、bake(烘,焙)、cut(切)、box(打包)
- 3、完成披萨店订购功能
2.1.1、传统模式
先看下不使用 工厂模式的设计
public class ASimpleFactory {
public static void main(String[] args) {
Order order = new Order();
}
}
// 抽象Pizza
@Data
abstract class Pizza {
// 披萨名字
protected String name;
// 准备原材料 不同的披萨是不一样的 所以是 抽象方法
public abstract void prepare();
public void bake() {
System.out.println(name + "baking");
}
public void cut() {
System.out.println(name + "cutting");
}
public void box() {
System.out.println(name + "boxing");
}
}
class GreekPizza extends Pizza {
@Override
public void prepare() {
System.out.println("制作GreekPizza 准备原材料");
}
}
class CheesePizza extends Pizza {
@Override
public void prepare() {
System.out.println("制作 CheesePizza 准备原材料");
}
}
// 1、新增披萨种类 胡椒披萨
class PepperPizza extends Pizza {
@Override
public void prepare() {
System.out.println("制作 PepperPizza 准备原材料");
}
}
// 订购 披萨
class Order {
// 构造器
public Order() {
Pizza pizza = null;
String orderType;
do {
orderType = getType();
if (orderType.equals("greek")) {
pizza = new GreekPizza();
pizza.setName("希腊 披萨");
} else if (orderType.equals("cheese")) {
pizza = new CheesePizza();
pizza.setName("奶酪披萨");
} else if (orderType.equals("pepper")) {
// 2、新增 披萨种类
pizza = new PepperPizza();
pizza.setName("胡椒披萨");
} else {
break;
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} while (true);
}
// 获取用户订购披萨的种类
private String getType() {
try {
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza 种类:");
String str = strin.readLine();
return str;
} catch (IOException e) {
e.printStackTrace();
return "";
}finally {
}
}
}
解释代码:
首先 披萨的制作有prepare(预备)、bake(烘,焙)、cut(切)、box(打包),其中只有 预备原材料是每个披萨都不同的操作
所以把 抽象一个披萨类 让每种 披萨 继承这个类 重写 预备的方法
// 抽象Pizza
@Data
abstract class Pizza {
// 披萨名字
protected String name;
// 准备原材料 不同的披萨是不一样的 所以是 抽象方法
public abstract void prepare();
public void bake() {
System.out.println(name + "baking");
}
public void cut() {
System.out.println(name + "cutting");
}
public void box() {
System.out.println(name + "boxing");
}
}
class GreekPizza extends Pizza {
@Override
public void prepare() {
System.out.println("制作GreekPizza 准备原材料");
}
}
class CheesePizza extends Pizza {
@Override
public void prepare() {
System.out.println("制作 CheesePizza 准备原材料");
}
}
// 1、新增披萨种类 胡椒披萨
class PepperPizza extends Pizza {
@Override
public void prepare() {
System.out.println("制作 PepperPizza 准备原材料");
}
}
接下来是
用户手动输入订单类型的方法
// 获取用户订购披萨的种类
private String getType() {
try {
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza 种类:");
String str = strin.readLine();
return str;
} catch (IOException e) {
e.printStackTrace();
return "";
}finally {
}
}
最后 订单的操作:
Order类中的无参构造器,在new一个Order是 会调用这个无参构造器的方法,在此方法中,先获取订单的类型,根据类型判断调用 哪个披萨对象。
public Order() {
Pizza pizza = null;
String orderType;
do {
orderType = getType();
if (orderType.equals("greek")) {
pizza = new GreekPizza();
pizza.setName("希腊 披萨");
} else if (orderType.equals("cheese")) {
pizza = new CheesePizza();
pizza.setName("奶酪披萨");
} else if (orderType.equals("pepper")) {
// 2、新增 披萨种类
pizza = new PepperPizza();
pizza.setName("胡椒披萨");
} else {
break;
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} while (true);
}
主方法:
public static void main(String[] args) {
Order order = new Order();
}
传统模式:
- 优点:好理解、简单易操作
- 缺点:违反了 开闭原则 ,对扩展开放,对修改关闭,给类增加新的功能,尽量不改或稍改代码
如:新增 披萨种类 需改两处代码
2.1.2、简单工厂模式
较于传统的模式 其 改进的思路:
把创建Pizza的对象封装到 一个类中,这样有新的种类,只需要修改该类即可
理解:简单工厂:
工厂生产 产品,所以单独有个工厂类 主要负责 制作产品,如 pizza 工厂 类 生成 pizza对象
public class BSimpleFactory {
// 订购 披萨
public static void main(String[] args) {
new OrderB(new SimpleFactory());
}
}
// 创建简单工厂类
class SimpleFactory{
// 根据 orderType 返回 对应的披萨对象
public PizzaB createPizza(String orderType){
System.out.println("简单工厂模式");
PizzaB pizza = null;
if (orderType.equals("greek")) {
pizza = new GreekPizzaB();
pizza.setName("希腊 披萨");
} else if (orderType.equals("cheese")) {
pizza = new CheesePizzaB();
pizza.setName("奶酪披萨");
} else if (orderType.equals("pepper")) {
// 2、新增 披萨种类
pizza = new PepperPizzaB();
pizza.setName("胡椒披萨");
}
return pizza;
}
}
// 抽象Pizza
@Data
abstract class PizzaB {
// 披萨名字
protected String name;
// 准备原材料 不同的披萨是不一样的 所以是 抽象方法
public abstract void prepare();
public void bake() {
System.out.println(name + "baking");
}
public void cut() {
System.out.println(name + "cutting");
}
public void box() {
System.out.println(name + "boxing");
}
}
class GreekPizzaB extends PizzaB {
@Override
public void prepare() {
System.out.println("制作GreekPizza 准备原材料");
}
}
class CheesePizzaB extends PizzaB {
@Override
public void prepare() {
System.out.println("制作 CheesePizza 准备原材料");
}
}
// 1、新增披萨种类 胡椒披萨
class PepperPizzaB extends PizzaB {
@Override
public void prepare() {
System.out.println("制作 PepperPizza 准备原材料");
}
}
// 订购 披萨
class OrderB {
// 定义一个简单工厂对象
SimpleFactory simpleFactory;
PizzaB pizza = null;
// 这使用的是聚合 通过构造器传入 SimpleFactory 对象,也可以直接new这个对象(组合关系)
public OrderB(SimpleFactory simpleFactory){
setFactory(simpleFactory);
}
public void setFactory(SimpleFactory simpleFactory){
this.simpleFactory = simpleFactory;
String orderType = "";// 用户输入的
do {
orderType = getType();
pizza = this.simpleFactory.createPizza(orderType);
if (pizza != null){
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}else {
System.out.println("订单失败");
break;
}
}while (true);
}
// 获取用户订购披萨的种类
private String getType() {
try {
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza 种类:");
String str = strin.readLine();
return str;
} catch (IOException e) {
e.printStackTrace();
return "";
}finally {
}
}
}
解读代码:
1、和传统模式一样 抽象pizza类 让各种pizza继承他重写预选原材料的方法。 以及获取用户输入披萨类型的方法。
2、重点来了:
创建一个简单工厂类,由这个类 来决定 披萨类型(也就是工厂生产的产品类型),有制作披萨的方法
class SimpleFactory{
// 根据 orderType 返回 对应的披萨对象
public PizzaB createPizza(String orderType){
System.out.println("简单工厂模式");
PizzaB pizza = null;
if (orderType.equals("greek")) {
pizza = new GreekPizzaB();
pizza.setName("希腊 披萨");
} else if (orderType.equals("cheese")) {
pizza = new CheesePizzaB();
pizza.setName("奶酪披萨");
} else if (orderType.equals("pepper")) {
// 2、新增 披萨种类
pizza = new PepperPizzaB();
pizza.setName("胡椒披萨");
}
return pizza;
}
}
3、订单类
首先 定义成员变量 工程类对象 和披萨类对象
SimpleFactory simpleFactory;
PizzaB pizza = null;
有参构造器参数为 工厂类,这样 创建这个订单类对象的时候 就要传一个 工程类的对象
// 这使用的是聚合 通过构造器传入 SimpleFactory 对象,也可以直接new这个对象(组合关系)
public OrderB(SimpleFactory simpleFactory){
setFactory(simpleFactory);
}
然后 set方法 将传入的工厂类对象 给 成员变量(工厂类) 并且调用这个工厂类 的制作披萨的方法
public void setFactory(SimpleFactory simpleFactory){
this.simpleFactory = simpleFactory;
String orderType = "";// 用户输入的
do {
orderType = getType();
pizza = this.simpleFactory.createPizza(orderType);
if (pizza != null){
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}else {
System.out.println("订单失败");
break;
}
}while (true);
}
2.1.3、静态工厂
简单工厂模式 也叫 静态工厂模式
和 上述简单工厂只有这两处改动
1、工厂类 中方法为 静态方法
// 创建静态工厂类
class SimpleFactoryC{
// 根据 orderType 返回 对应的披萨对象
public static PizzaC createPizza(String orderType){
System.out.println("简单工厂模式");
PizzaC pizza = null;
if (orderType.equals("greek")) {
pizza = new GreekPizzaC();
pizza.setName("希腊 披萨");
} else if (orderType.equals("cheese")) {
pizza = new CheesePizzaC();
pizza.setName("奶酪披萨");
} else if (orderType.equals("pepper")) {
// 2、新增 披萨种类
pizza = new PepperPizzaC();
pizza.setName("胡椒披萨");
}
return pizza;
}
}
2、 改为静态方法的目的是 订单处 不需要工厂对象也可以调用该方法
// 定义一个简单工厂对象
PizzaC pizza = null;
public OrderC(){
String orderType = "";// 用户输入的
do {
orderType = getType();
pizza = SimpleFactoryC.createPizza(orderType);
if (pizza != null){
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}else {
System.out.println("订单失败");
break;
}
}while (true);
}
2.2、工厂方法模式
1、看一个新的需求
披萨项目新的需求:客户在点披萨时,可以点不同口味的披萨,
比如 北京的奶酪 pizza、北京的胡椒 pizza 或 者是伦敦的奶酪 pizza、伦敦的胡椒 pizza
思路1、
使用简单工厂模式,创建不同的简单工厂类,比如 BJPizzaSimpleFactory、LDPizzaSimpleFactory 等等.
从当前 这个案例来说,也是可以的,但是考虑到项目的规模,以及软件的可维护性、可扩展性并不是特别好
思路2、
使用工厂方法模式
工厂方法模式 介绍:
- 工厂方法模式设计方案:将披萨项目的实例化功能抽象成抽象方法,在不同的口味点餐子类(子工厂)中具体实现。
2) 工厂方法模式:定义了一个创建对象的抽象方法,由子类决定要实例化的类。
理解:
抽象工厂方法就是 首先有个抽象类为 总工厂。然后里面有个抽象方法, 由不同的子工厂继承这个类并且实现该抽象方法,从而得到需要的产品。如
代码:
public class AMethodFactory {
public static void main(String[] args) {
// 创建北京口味的Pizza
new BJOrderPizza();
}
}
// 抽象Pizza
@Data
abstract class Pizza {
// 披萨名字
protected String name;
// 准备原材料 不同的披萨是不一样的 所以是 抽象方法
public abstract void prepare();
public void bake() {
System.out.println(name + "baking");
}
public void cut() {
System.out.println(name + "cutting");
}
public void box() {
System.out.println(name + "boxing");
}
}
class BJCheesePizza extends Pizza{
@Override
public void prepare() {
setName("北京的奶酪pizza");
System.out.println("北京的奶酪pizza 准备原材料");
}
}
class BJPepperPizza extends Pizza{
@Override
public void prepare() {
setName("北京的胡椒pizza");
System.out.println("北京的胡椒pizza 准备原材料");
}
}
class LDCheesePizza extends Pizza{
@Override
public void prepare() {
setName("伦敦的奶酪pizza");
System.out.println("伦敦的奶酪pizza 准备原材料");
}
}
class LDPepperPizza extends Pizza{
@Override
public void prepare() {
setName("伦敦的胡椒pizza");
System.out.println("伦敦的胡椒pizza 准备原材料");
}
}
// 披萨订单
abstract class OrderPizza{
// 定义一个抽象 工厂方法,createPizza, 让各个工厂子类实现
abstract Pizza createPizza(String orderType);
// 构造器
public OrderPizza(){
Pizza pizza = null;
String orderType;// 披萨类型
do {
orderType = getType();
pizza = createPizza(orderType);
// 制作过程
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}while (true);
}
// 获取用户订购披萨的种类
private String getType() {
try {
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza 种类:");
String str = strin.readLine();
return str;
} catch (IOException e) {
e.printStackTrace();
return "";
}finally {
}
}
}
// 工厂子类 北京Order
class BJOrderPizza extends OrderPizza{
@Override
Pizza createPizza(String orderType) {
System.out.println("工厂方法模式");
Pizza pizza = null;
if (orderType.equals("cheese")) {
pizza = new BJCheesePizza();
} else if (orderType.equals("pepper")) {
pizza = new BJPepperPizza();
}
return pizza;
}
}
// 工厂子类 北京Order
class LDOrderPizza extends OrderPizza{
@Override
Pizza createPizza(String orderType) {
System.out.println("工厂方法模式");
Pizza pizza = null;
if (orderType.equals("cheese")) {
pizza = new LDCheesePizza();
} else if (orderType.equals("pepper")) {
pizza = new LDPepperPizza();
}
return pizza;
}
}
解读代码:
1、抽象pizza类 让各种pizza继承他重写预选原材料的方法
// 抽象Pizza
@Data
abstract class Pizza {
// 披萨名字
protected String name;
// 准备原材料 不同的披萨是不一样的 所以是 抽象方法
public abstract void prepare();
public void bake() {
System.out.println(name + "baking");
}
public void cut() {
System.out.println(name + "cutting");
}
public void box() {
System.out.println(name + "boxing");
}
}
class BJCheesePizza extends Pizza{
@Override
public void prepare() {
setName("北京的奶酪pizza");
System.out.println("北京的奶酪pizza 准备原材料");
}
}
class BJPepperPizza extends Pizza{
@Override
public void prepare() {
setName("北京的胡椒pizza");
System.out.println("北京的胡椒pizza 准备原材料");
}
}
class LDCheesePizza extends Pizza{
@Override
public void prepare() {
setName("伦敦的奶酪pizza");
System.out.println("伦敦的奶酪pizza 准备原材料");
}
}
class LDPepperPizza extends Pizza{
@Override
public void prepare() {
setName("伦敦的胡椒pizza");
System.out.println("伦敦的胡椒pizza 准备原材料");
}
}
重点来啦
抽象 披萨订单 类 可以看作是总工厂,内有 抽象方法:制作pizza的方法,由各个工厂子类还实现该方法
// 披萨订单
abstract class OrderPizza{
// 定义一个抽象 工厂方法,createPizza, 让各个工厂子类实现
abstract Pizza createPizza(String orderType);
// 构造器
public OrderPizza(){
Pizza pizza = null;
String orderType;// 披萨类型
do {
orderType = getType();
pizza = createPizza(orderType);
// 制作过程
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}while (true);
}
// 获取用户订购披萨的种类
private String getType() {
try {
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza 种类:");
String str = strin.readLine();
return str;
} catch (IOException e) {
e.printStackTrace();
return "";
}finally {
}
}
}
// 工厂子类 北京Order
class BJOrderPizza extends OrderPizza{
@Override
Pizza createPizza(String orderType) {
System.out.println("工厂方法模式");
Pizza pizza = null;
if (orderType.equals("cheese")) {
pizza = new BJCheesePizza();
} else if (orderType.equals("pepper")) {
pizza = new BJPepperPizza();
}
return pizza;
}
}
// 工厂子类 北京Order
class LDOrderPizza extends OrderPizza{
@Override
Pizza createPizza(String orderType) {
System.out.println("工厂方法模式");
Pizza pizza = null;
if (orderType.equals("cheese")) {
pizza = new LDCheesePizza();
} else if (orderType.equals("pepper")) {
pizza = new LDPepperPizza();
}
return pizza;
}
}
北京口味的披萨
public static void main(String[] args) {
// 创建北京口味的Pizza
new BJOrderPizza();
}
2.3、抽象工厂模式
- 抽象工厂模式:定义了一个 interface 用于创建相关或有依赖关系的对象簇,而无需指明具体的类
- 抽象工厂模式可以将简单工厂模式和工厂方法模式进行整合。
- 从设计层面看,抽象工厂模式就是对简单工厂模式的改进(或者称为进一步的抽象)。
- 将工厂抽象成两层,AbsFactory(抽象工厂) 和 具体实现的工厂子类。程序员可以根据创建对象类型使用对应 的工厂子类。
这样将单个的简单工厂类变成了工厂簇,更利于代码的维护和扩展。
public class aAbstractFactory {
public static void main(String[] args) {
new OrderPizza(new BJFactory());
}
}
// 抽象Pizza
@Data
abstract class Pizza {
// 披萨名字
protected String name;
// 准备原材料 不同的披萨是不一样的 所以是 抽象方法
public abstract void prepare();
public void bake() {
System.out.println(name + "baking");
}
public void cut() {
System.out.println(name + "cutting");
}
public void box() {
System.out.println(name + "boxing");
}
}
class BJCheesePizza extends Pizza {
@Override
public void prepare() {
setName("北京的奶酪pizza");
System.out.println("北京的奶酪pizza 准备原材料");
}
}
class BJPepperPizza extends Pizza {
@Override
public void prepare() {
setName("北京的胡椒pizza");
System.out.println("北京的胡椒pizza 准备原材料");
}
}
class LDCheesePizza extends Pizza {
@Override
public void prepare() {
setName("伦敦的奶酪pizza");
System.out.println("伦敦的奶酪pizza 准备原材料");
}
}
class LDPepperPizza extends Pizza {
@Override
public void prepare() {
setName("伦敦的胡椒pizza");
System.out.println("伦敦的胡椒pizza 准备原材料");
}
}
// 一个抽象工厂模式的抽象层
interface AbsFactory {
// 让下面的工厂子类来具体实现
public Pizza createPizza(String orderType);
}
// 北京的工厂子类
class BJFactory implements AbsFactory {
@Override
public Pizza createPizza(String orderType) {
System.out.println("抽象工厂模式");
Pizza pizza = null;
if (orderType.equals("cheese")) {
pizza = new BJCheesePizza();
} else if (orderType.equals("pepper")) {
pizza = new BJPepperPizza();
}
return pizza;
}
}
// 伦敦的工厂子类
class LDFactory implements AbsFactory {
@Override
public Pizza createPizza(String orderType) {
System.out.println("抽象工厂模式");
Pizza pizza = null;
if (orderType.equals("cheese")) {
pizza = new LDCheesePizza();
} else if (orderType.equals("pepper")) {
pizza = new LDPepperPizza();
}
return pizza;
}
}
class OrderPizza{
AbsFactory factory;
// 构造器
public OrderPizza(AbsFactory factory){
setFactory(factory);
}
public void setFactory(AbsFactory factory){
Pizza pizza = null;
String orderType = ""; // 用户输入
this.factory = factory;
do {
orderType = getType();
// factory 可能是北京的工厂子类 也可能是 伦敦的工厂子类
pizza = factory.createPizza(orderType);
// 制作过程
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}while (true);
}
// 获取用户订购披萨的种类
private String getType() {
try {
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza 种类:");
String str = strin.readLine();
return str;
} catch (IOException e) {
e.printStackTrace();
return "";
}finally {
}
}
}
解读代码
1、抽象pizza类 让各种pizza继承他重写预选原材料的方法
// 抽象Pizza
@Data
abstract class Pizza {
// 披萨名字
protected String name;
// 准备原材料 不同的披萨是不一样的 所以是 抽象方法
public abstract void prepare();
public void bake() {
System.out.println(name + "baking");
}
public void cut() {
System.out.println(name + "cutting");
}
public void box() {
System.out.println(name + "boxing");
}
}
class BJCheesePizza extends Pizza {
@Override
public void prepare() {
setName("北京的奶酪pizza");
System.out.println("北京的奶酪pizza 准备原材料");
}
}
class BJPepperPizza extends Pizza {
@Override
public void prepare() {
setName("北京的胡椒pizza");
System.out.println("北京的胡椒pizza 准备原材料");
}
}
class LDCheesePizza extends Pizza {
@Override
public void prepare() {
setName("伦敦的奶酪pizza");
System.out.println("伦敦的奶酪pizza 准备原材料");
}
}
class LDPepperPizza extends Pizza {
@Override
public void prepare() {
setName("伦敦的胡椒pizza");
System.out.println("伦敦的胡椒pizza 准备原材料");
}
}
重点来了
定义一个抽象总工厂 接口 。内有方法,由具体子工厂来实现该方法
// 一个抽象工厂模式的抽象层
interface AbsFactory {
// 让下面的工厂子类来具体实现
public Pizza createPizza(String orderType);
}
// 北京的工厂子类
class BJFactory implements AbsFactory {
@Override
public Pizza createPizza(String orderType) {
System.out.println("抽象工厂模式");
Pizza pizza = null;
if (orderType.equals("cheese")) {
pizza = new BJCheesePizza();
} else if (orderType.equals("pepper")) {
pizza = new BJPepperPizza();
}
return pizza;
}
}
// 伦敦的工厂子类
class LDFactory implements AbsFactory {
@Override
public Pizza createPizza(String orderType) {
System.out.println("抽象工厂模式");
Pizza pizza = null;
if (orderType.equals("cheese")) {
pizza = new LDCheesePizza();
} else if (orderType.equals("pepper")) {
pizza = new LDPepperPizza();
}
return pizza;
}
}
订单类 成员变量为 接口AbsFactory ,由 有参构造器的参数也是AbsFactory,new的时候要传入AbsFactory对象
然后由set方法将传的AbsFactory对象赋值给 成员变量AbsFactory。
class OrderPizza{
AbsFactory factory;
// 构造器
public OrderPizza(AbsFactory factory){
setFactory(factory);
}
public void setFactory(AbsFactory factory){
Pizza pizza = null;
String orderType = ""; // 用户输入
this.factory = factory;
do {
orderType = getType();
// factory 可能是北京的工厂子类 也可能是 伦敦的工厂子类
pizza = factory.createPizza(orderType);
// 制作过程
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}while (true);
}
// 获取用户订购披萨的种类
private String getType() {
try {
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza 种类:");
String str = strin.readLine();
return str;
} catch (IOException e) {
e.printStackTrace();
return "";
}finally {
}
}
}
mian
public static void main(String[] args) {
new OrderPizza(new BJFactory());
}
工厂模式在 JDK中的应用
Calendar 类中,就使用了简单工厂模式
工厂模式小结
- 工厂模式的意义 将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到和主项目的依赖关系的解耦。从而提高项 目的扩展和维护性。
- 三种工厂模式 (简单工厂模式、工厂方法模式、抽象工厂模式)
- 设计模式的依赖抽象原则
建议:
- 创建对象实例时,不要直接 new 类, 而是把这个 new 类的动作放在一个工厂的方法中,并返回。有的书上说, 变量不要直接持有具体类的引用。
- 不要让类继承具体类,而是继承抽象类或者是实现 interface(接口)
- 不要覆盖基类中已经实现的方法。
三、原型模式
原型模式:
就是将一个对象进行复制。深拷贝
Java 中 Object 类是所有类的根类,Object 类提供了一个 clone()方法,该方法可以将一个 Java 对象复制 一份,但是需要实现 clone 的 Java 类必须要实现一个接口 Cloneable,
该接口表示该类能够复制且具有复制的能力
比如:孙悟空 拔出猴毛,变出其他的孙悟空,一模一样
注意:
Cloneable的 clone方法对应 基本数据类型和String类型 是深拷贝
但是 对于 引用数据类型就是浅拷贝 ,不符合要求
public class BPrototype {
public static void main(String[] args) {
SheepB sheep1 = new SheepB("tom", 1, "白色");
SheepB sheep2 = (SheepB)sheep1.clone();
SheepB sheep3 = (SheepB)sheep1.clone();
System.out.println(sheep1);
System.out.println(sheep2);
System.out.println(sheep3);
}
}
@Data
class SheepB implements Cloneable{
String name;
int age;
String color;
public SheepB(String name, int age, String color) {
this.age = age;
this.name = name;
this.color = color;
}
// 克隆 该对象,使用 默认的clone 方法来完成
@Override
protected Object clone(){
SheepB sheepB = null;
try {
sheepB = (SheepB)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return sheepB;
}
}
解读代码:
需要copy的类 实现 Cloneable接口 并重写其clone方法()
// 克隆 该对象,使用 默认的clone 方法来完成
@Override
protected Object clone(){
SheepB sheepB = null;
try {
sheepB = (SheepB)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return sheepB;
}
深拷贝 和 浅拷贝
深拷贝
不仅复制对象的基本类,同时也复制原对象中的对象.就是说完全是新对象产生的.
浅拷贝
只复制对象的基本类型,对象类型,仍属于原来的引用
比如:B 复制 A
- 浅拷贝:修改A 的值 B 的值也会改变 因为是同一个引用地址 : 克隆羊是浅拷贝
- 深拷贝:不会改变,因为B 是一个新的对象,和A 没关系了
深拷贝的实现方法
- 1、 重写 clone 实现深拷贝
- 2、通过 对象序列化和反序列化实现深拷贝
3.1、重写 clone 实现深拷贝
- 1、基本类型 和 String 之间用 默认的clone方法
- 2、A类中有 引用对象B 就要套一下了
首先在引用对象B 的类中也重写 clone,
然后在A clone方法中,调用B的clone方法后 赋值给A 的引用对象B
缺点:
如果引用对象中还有引用的话,就很麻烦 这个方法不推荐
public static void main(String[] args) throws CloneNotSupportedException {
DeepProtoType p = new DeepProtoType();
p.name = "松江";
p.deepCloneableTarget = new DeepCloneableTarget("大","小");
DeepProtoType p2 = (DeepProtoType)p.clone();
System.out.println(p.deepCloneableTarget.hashCode());
System.out.println(p2.deepCloneableTarget.hashCode());
}
}
class DeepCloneableTarget implements Cloneable {
private String cloneName;
private String cloneClass;
//构造器
public DeepCloneableTarget(String cloneName, String cloneClass) {
this.cloneName = cloneName;
this.cloneClass = cloneClass;
}
//因为该类的属性,都是 String , 因此我们这里使用默认的 clone 完成即可
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class DeepProtoType implements Cloneable {
public String name; //String 属性
public DeepCloneableTarget deepCloneableTarget;// 引用类型
public DeepProtoType() {
super();
}
//深拷贝 - 方式 1 使用 clone 方法
@Override
protected Object clone() throws CloneNotSupportedException {
Object deep = null;
//这里完成对基本数据类型(属性)和 String 的克隆
deep = super.clone();
//对引用类型的属性,进行单独处理
DeepProtoType deepProtoType = (DeepProtoType) deep;
deepProtoType.deepCloneableTarget = (DeepCloneableTarget) deepCloneableTarget.clone();
return deepProtoType;
}
}
新对象引用对象和原引用对象的地址已经发生改变
3.2、深拷贝 通过对象的序列化和反序列化实现(推荐)
public class DPrototype {
public static void main(String[] args) throws CloneNotSupportedException {
DeepProtoTypeD p = new DeepProtoTypeD();
p.name = "松江";
p.deepCloneableTarget = new DeepCloneableTargetD("大", "小");
DeepProtoTypeD p2 = (DeepProtoTypeD) p.deepClone();
System.out.println(p.deepCloneableTarget.hashCode());
System.out.println(p2.deepCloneableTarget.hashCode());
}
}
class DeepCloneableTargetD implements Serializable{
private String cloneName;
private String cloneClass;
//构造器
public DeepCloneableTargetD(String cloneName, String cloneClass) {
this.cloneName = cloneName;
this.cloneClass = cloneClass;
}
}
class DeepProtoTypeD implements Serializable {
public String name; //String 属性
public DeepCloneableTargetD deepCloneableTarget;// 引用类型
public DeepProtoTypeD() {
super();
}
// 深拷贝 -方法2、通过对象的序列化和反序列化实现(推荐)
Object deepClone() {
//创建流对象
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
//序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this);//当前这个对象以对象流的方式输出
//反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
DeepProtoTypeD copyObj = (DeepProtoTypeD) ois.readObject();
return copyObj;
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
return null;
} finally {
//关闭流
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch (Exception e2) {
// TODO: handle exception
System.out.println(e2.getMessage());
}
}
}
}
四、建造者模式
产品和具体生产产品的细节 分开
建造者模式
- 又叫生成器模式,可以将复杂对象的建造过程抽取出来(抽象类别),使这个抽象过程的不同实现方式可以构造出不同表现的对象;
- 建造者模式 是一步一步创建一个复杂的对象,它允许用户值通过指定复杂对象的类型和内容就可以构建他们,用户不需要知道内部具体的构建细节
- 如 造个汽车,我们只要知道把方向盘、轮胎等组装起来就ok,不需要知道 方向盘等 是怎么造出来的
建造模式的四个角色
- Product(产品角色):一个具体的产品对象,内含产品的属性
- 、Builder(抽象建造者):创建一个Product对象的各个部件指定的接口 (和 Product组合依赖【new 】)
- ConcreteBuilder(具体建造者):实现接口,产品的细节操作
- 、Director(指挥者):构建一个使用Builder接口的对象。【和 Builder 聚合依赖】
指挥者作用:
它主要是用于创建一个复杂的对象。 两个作用:1、隔离客户与对象的生产过程 2、负责控制产品对象的生产过程
案例:盖房子
- 需要建房子【产品】:这一过程为打桩、砌墙、封顶。【细节】
- 房子有各种各样的,比如普通房,高楼,别墅,各种房子的过程虽然一样,但是要求不要相同的.
首先看个 传统设计代码
public class ABuilderPattern {
public static void main(String[] args) {
new CommonHouse().build();
}
}
// 抽象类房子
abstract class House{
// 打地基
abstract void buildBasic();
// 砌墙
abstract void buildWalls();
// 封顶
abstract void roofed();
// 盖房子方法
void build(){
buildBasic();
buildWalls();
roofed();
}
}
// 盖个普通的房子
class CommonHouse extends House {
@Override
void buildBasic() {
System.out.println("普通房子打地基");
}
@Override
void buildWalls() {
System.out.println("普通房子砌墙");
}
@Override
void roofed() {
System.out.println("普通房子封顶");
}
}
代码解读:
传统方式的问题分析
- 优点是比较好理解,简单易操作。
- 设计的程序结构,过于简单,没有设计缓存层对象,程序的扩展和维护不好.
也就是说,这种设计方案,把产 品(即:房子) 和 创建产品的过程(即:建房子流程) 封装在一起,耦合性增强了。
使用建造者模式
public class BBuilderPattern {
public static void main(String[] args) {
new HouseDirector(new BCommonHouse()).constructHouse();
}
}
// 1、 Product产品角色(房子) 内含产品的属性
@Data
class BHouse{
// 地基
String basic;
// 墙
String wall;
// 屋顶
String roofed;
}
// 2、抽象建造者Build:创建一个Product对象的各个部件指定的接口(和 Product组合依赖【new 】)
// 建造房子的细节
abstract class HouseBuild{
BHouse bHouse = new BHouse();
// 打地基
abstract void buildBasic();
// 砌墙
abstract void buildWalls();
// 封顶
abstract void roofed();
// 建造完成房子,将产品(房子)返回
BHouse build(){
return bHouse;
}
}
// 3、ConcreteBuilder(具体建造者)【普通房子】:实现接口,构建和装备各个部件
class BCommonHouse extends HouseBuild{
@Override
void buildBasic() {
System.out.println("普通房子打地基");
}
@Override
void buildWalls() {
System.out.println("普通房子砌墙");
}
@Override
void roofed() {
System.out.println("普通房子封顶");
}
}
// 3、ConcreteBuilder(具体建造者)【别墅】:实现接口,构建和装备各个部件
class BVillaHouse extends HouseBuild{
@Override
void buildBasic() {
System.out.println("别墅打地基");
}
@Override
void buildWalls() {
System.out.println("别墅砌墙");
}
@Override
void roofed() {
System.out.println("别墅封顶");
}
}
// 4、Director(指挥者):构建一个使用Builder接口的对象
// 房屋建造指挥者HouseDirector 和 HouseBuild 聚合依赖
class HouseDirector{
HouseBuild houseBuild;
public HouseDirector(HouseBuild houseBuild){
this.houseBuild = houseBuild;
}
// 如何处理建造房子的流程,交给指挥者决定 ,造好返回产品(房子)
public BHouse constructHouse(){
houseBuild.buildBasic();
houseBuild.buildWalls();
houseBuild.roofed();
return houseBuild.build();
}
}
解读代码:
建造者模式的 四大角色
- Product产品角色(房子) 内含产品的属性
@Data
class BHouse{
// 地基
String basic;
// 墙
String wall;
// 屋顶
String roofed;
}
- 抽象建造者Build:创建一个Product对象的各个部件指定的接口(和 Product组合依赖【new 】)
就是 抽象 盖房子的细节操作的方法
abstract class HouseBuild{
BHouse bHouse = new BHouse();
// 打地基
abstract void buildBasic();
// 砌墙
abstract void buildWalls();
// 封顶
abstract void roofed();
// 建造完成房子,将产品(房子)返回
BHouse build(){
return bHouse;
}
}
- ConcreteBuilder(具体建造者)【普通房子、别墅】:实现接口,构建和装备各个部件.
// 3、ConcreteBuilder(具体建造者)【普通房子】:实现接口,构建和装备各个部件
class BCommonHouse extends HouseBuild{
@Override
void buildBasic() {
System.out.println("普通房子打地基");
}
@Override
void buildWalls() {
System.out.println("普通房子砌墙");
}
@Override
void roofed() {
System.out.println("普通房子封顶");
}
}
// 3、ConcreteBuilder(具体建造者)【别墅】:实现接口,构建和装备各个部件
class BVillaHouse extends HouseBuild{
@Override
void buildBasic() {
System.out.println("别墅打地基");
}
@Override
void buildWalls() {
System.out.println("别墅砌墙");
}
@Override
void roofed() {
System.out.println("别墅封顶");
}
}
- Director(指挥者):构建一个使用Builder接口的对象
房屋建造指挥者HouseDirector 和 HouseBuild 聚合依赖
// 4、Director(指挥者):构建一个使用Builder接口的对象
// 房屋建造指挥者HouseDirector 和 HouseBuild 聚合依赖
class HouseDirector{
HouseBuild houseBuild;
public HouseDirector(HouseBuild houseBuild){
this.houseBuild = houseBuild;
}
// 如何处理建造房子的流程,交给指挥者决定 ,造好返回产品(房子)
public BHouse constructHouse(){
houseBuild.buildBasic();
houseBuild.buildWalls();
houseBuild.roofed();
return houseBuild.build();
}
}
实现房子产品 和 盖房子的细节 进行分离
五、适配器模式
什么是适配器:
- 如 t-c的充电器线给苹果手机充电,就需要 一个转换器 tc输入连接转换器,苹果线输出 转换器 ,转换器就是适配器。
什么是适配器模式
- 将 A类的接口 转换为 期望 的另一个接口表示,让两个不匹配的两个类 可以在一起工作 别名 包装类 (wrapper)
- 属于 结构型模式
- 分为三类 类适配器模式、对象适配器模式 、接口适配器模式
功能:让不匹配的类 可以协调工作
注意事项
- 三种命名方式,是根据 src 是以怎样的形式给到 Adapter(在 Adapter 里的形式)来命名的。
- 类适配器:以类给到,在 Adapter 里,就是将 src 当做类,继承
对象适配器:以对象给到,在 Adapter 里,将 src 作为一个对象,持有
接口适配器:以接口给到,在 Adapter 里,将 src 作为一个接口,实现 - Adapter 模式最大的作用还是将原本不兼容的接口融合在一起工作。
案例
被适配:220V电压(Voltage) 充电器(适配器) 目的 5V电压 给手机充电
5.1、类适配器模式
介绍
Adapter类【适配器】,通过继承 src 类【被适配的累】,实现 dst类接口【目标类】,完成 src -> dst 的适配
适配器 获取 220Vde 电压 将其改为5V输出
public class AAdapterPattern {
public static void main(String[] args) {
System.out.println("类适配器模式");
// 用适配器 给手机充电
new Phone().charging(new VoltageAdapter());
}
}
// 1、 被适配 220V电压
class Voltage220V{
// 输出 220V的电压
int output220V(){
int src = 220;
return src;
}
}
// 2、设配接口 输出5V
interface Voltage5V{
int out5V();
}
// 3、适配器类 将220V 电压 转换为 5V
class VoltageAdapter extends Voltage220V implements Voltage5V{
@Override
public int out5V() {
// 拿到 220V 电压
int srcV = output220V();
// 适配器 将220转为5V
int dstV = srcV / 44;
return dstV;
}
}
// 手机
class Phone{
// 充电
void charging(Voltage5V v){
if (v.out5V() ==5){
System.out.println("充电...");
}else {
System.out.println("NONONO");
}
}
}
代码解读
通过 设配器 VoltageAdapter 将 Voltage220V、Phone这两个不匹配的类 可以协调工作
- 被适配的类 220V电压
class Voltage220V{
// 输出 220V的电压
int output220V(){
int src = 220;
return src;
}
}
- 目的类 接口 5V 电压
// 2、设配接口 输出5V
interface Voltage5V{
int out5V();
}
- 适配器类 继承 被适配类Voltage220V 实现 目的 接口 Voltage5V
重写 目的接口Voltage5V 的方法
获取适配类 Voltage220V 的220电压 将其改为 5 V 进行输出
// 3、适配器类 将220V 电压 转换为 5V
class VoltageAdapter extends Voltage220V implements Voltage5V{
@Override
public int out5V() {
// 拿到 220V 电压
int srcV = output220V();
// 适配器 将220转为5V
int dstV = srcV / 44;
return dstV;
}
}
手机
// 手机
class Phone{
// 充电
void charging(Voltage5V v){
if (v.out5V() ==5){
System.out.println("充电...");
}else {
System.out.println("NONONO");
}
}
}
5.2、对象适配器模式
介绍
- 根据“合成复用原则”,在系统中尽量使用关联关系(聚合)来替代继承关系。
- 对象适配器模式是适配器模式常用的一种
基本思路
和类适配器类模式相同,只是将 设配器类修改,不是继承被适配类,而是持有被适配的类的对象【聚合方式】
即:持有 src类 实现dst类的接口 完成 src -> dst的适配
BVoltageAdapter 不再继承 BVoltage220V类,使用聚合 在BVoltageAdapter中传入 BVoltage220V类对象
public class BAdapterPattern {
public static void main(String[] args) {
System.out.println("对象适配器模式");
// 用适配器 给手机充电
new BPhone().charging(new BVoltageAdapter(new BVoltage220V()));
}
}
// 1、 被适配 220V电压
class BVoltage220V{
// 输出 220V的电压
int output220V(){
int src = 220;
return src;
}
}
// 2、设配接口 输出5V
interface BVoltage5V{
int out5V();
}
// 3、对象适配器 将220V 电压 转换为 5V
class BVoltageAdapter implements BVoltage5V{
BVoltage220V bVoltage220V;
public BVoltageAdapter(BVoltage220V bVoltage220V){
this.bVoltage220V = bVoltage220V;
}
@Override
public int out5V() {
int dstV = 0;
if (null != bVoltage220V){
// 拿到 220V 电压
int srcV =bVoltage220V.output220V();
// 适配器 将220转为5V
dstV = srcV / 44;
}
return dstV;
}
}
// 手机
class BPhone{
// 充电
void charging(BVoltage5V v){
if (v.out5V() ==5){
System.out.println("充电...");
}else {
System.out.println("NONONO");
}
}
}
代码解读:
通过 聚合的方法将 被适配器对象传入 进行操作,不再是用继承了
// 3、对象适配器 将220V 电压 转换为 5V
class BVoltageAdapter implements BVoltage5V{
BVoltage220V bVoltage220V;
public BVoltageAdapter(BVoltage220V bVoltage220V){
this.bVoltage220V = bVoltage220V;
}
@Override
public int out5V() {
int dstV = 0;
if (null != bVoltage220V){
// 拿到 220V 电压
int srcV =bVoltage220V.output220V();
// 适配器 将220转为5V
dstV = srcV / 44;
}
return dstV;
}
}
5.3、接口适配模式
介绍
当不需要全部实现接口的方法时,可以先设计一个抽象的实现接口,
并为 该接口中的每个方法提供一个默认实现方法(空方法)
那么,该抽象类的子类 可以有选择的覆盖父类的某些方法 来实现需求。
适用:
一个接口不想使用其所有方法的情况
如:
i4接口 有很多方法,B抽象类实现 i4 接口,并实现I接口的所有方法【空方法】
主方法创建 B的实例,并有选择的 重写 需要使用的 接口方法
public class CAdapterPattern {
public static void main(String[] args) {
B b = new B(){
// 只需要重写需要使用的 接口方法
@Override
public void m1() {
super.m1();
System.out.println("使用了m1");
}
};
}
}
interface i4{
void m1();
void m2();
void m3();
void m4();
}
abstract class B implements i4{
@Override
public void m1() {
}
@Override
public void m2() {
}
@Override
public void m3() {
}
@Override
public void m4() {
}
}
六、桥接模式
介绍
- Bridge 模式基于类的最小设计原则,通过使用封装、聚合及继承等行为让不同的类承担不同的职责。
- 它的主要 特点是把抽象(Abstraction)与行为实现(Implementation)分离开来,从而可以保持各部分的独立性以及应对他们的 功能扩展
案例
- 在对不同手机类型的不同品牌实现操作编程(比如:开机、关机、上网,打电话等)
- 手机分为折叠式,直立式 旋转式 ,每种又分为 华为、小米、vivo
public class ABridgePattern {
public static void main(String[] args) {
Phone p1 = new FoldedPhone(new XiaoMi());
p1.open();
p1.call();
p1.close();
System.out.println("=================");
Phone p2 = new FoldedPhone(new Vivo());
p2.open();
p2.call();
p2.close();
System.out.println("=================");
Phone p3 = new UpRightPhone(new Vivo());
p3.open();
p3.call();
p3.close();
}
}
/**
* 品牌 接口
*/
interface Brand{
// 开机
void open();
// 关机
void close();
// 打电话
void call();
}
// 小米
class XiaoMi implements Brand{
@Override
public void open() {
System.out.println("小米开机");
}
@Override
public void close() {
System.out.println("小米关机");
}
@Override
public void call() {
System.out.println("小米打电话");
}
}
// Vivo
class Vivo implements Brand{
@Override
public void open() {
System.out.println("Vivo开机");
}
@Override
public void close() {
System.out.println("Vivo关机");
}
@Override
public void call() {
System.out.println("Vivo打电话");
}
}
// 抽象手机类
abstract class Phone{
// 品牌 组合关系
private Brand brand;
// 构造器
public Phone(Brand brand){
super();
this.brand = brand;
}
// 调用实现该手机的品牌的方法
void open(){
this.brand.open();
}
void close(){
this.brand.close();
}
void call(){
this.brand.call();
}
}
/**
* 折叠手机 继承抽象类
*
* 如何实现桥接的
* 折叠手机调用 open ,调用的是父类Phone的open,父类的open 调用的是 品牌的open
* == 折叠手机调用的 open 调用的是 品牌的open
*/
class FoldedPhone extends Phone{
public FoldedPhone(Brand brand) {
super(brand);
}
void open(){
super.open();
System.out.println("折叠手机");
}
void close(){
super.close();
System.out.println("折叠手机");
}
void call(){
super.call();
System.out.println("折叠手机");
}
}
// 新增 一个 直立式 样式 只要多这个对象
class UpRightPhone extends Phone{
public UpRightPhone(Brand brand) {
super(brand);
}
void open(){
super.open();
System.out.println("直立手机");
}
void close(){
super.close();
System.out.println("直立手机");
}
void call(){
super.call();
System.out.println("直立手机");
}
}
代码解读
1、定义接口 品牌类
interface Brand{
// 开机
void open();
// 关机
void close();
// 打电话
void call();
}
2、手机 实现 品牌
// 小米
class XiaoMi implements Brand{
@Override
public void open() {
System.out.println("小米开机");
}
@Override
public void close() {
System.out.println("小米关机");
}
@Override
public void call() {
System.out.println("小米打电话");
}
}
3、抽象手机类 并于品牌接口 组合关联
// 抽象手机类
abstract class Phone{
// 品牌 组合关系
private Brand brand;
// 构造器
public Phone(Brand brand){
super();
this.brand = brand;
}
// 调用实现该手机的品牌的方法
void open(){
this.brand.open();
}
void close(){
this.brand.close();
}
void call(){
this.brand.call();
}
}
4、 折叠手机 继承手机类
class FoldedPhone extends Phone{
public FoldedPhone(Brand brand) {
super(brand);
}
void open(){
super.open();
System.out.println("折叠手机");
}
void close(){
super.close();
System.out.println("折叠手机");
}
void call(){
super.call();
System.out.println("折叠手机");
}
}
关键来了:
- 如何实现桥接的
折叠手机调用 open ,调用的是父类Phone的open,父类的open 调用的是 品牌的open
== 折叠手机调用的 open 调用的是 品牌的open
Phone p1 = new FoldedPhone(new XiaoMi());
p1.open();
p1.call();
p1.close();
注意事项和细节
- 实现了抽象和实现部分的分离,从而极大的提供了系统的灵活性,让抽象部分和实现部分独立开来,这有助于 系统进行分层设计,从而产生更好的结构化系统。
- 对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了,其它的部分由具体业务来完成。
- 桥接模式替代多层继承方案,可以减少子类的个数,降低系统的管理和维护成本。
- 桥接模式的引入增加了系统的理解和设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设 计和编程
- 桥接模式要求正确识别出系统中两个独立变化的维度(抽象、和实现),因此其使用范围有一定的局限性,即需 要有这样的应用场景。
应用场景
- -JDBC 驱动程序
- -银行转账系统 转账分类: 网上转账,柜台转账,AMT 转账 转账用户类型:普通用户,银卡用户,金卡用户…
- -消息管理 消息类型:即时消息,延时消息 消息分类:手机短信,邮件消息,QQ 消息…
七、观察者模式
需求
- 气象站可以将每天测量到的温度,湿度,气压等等 以公告的形式发布出去(比如发布到自己的网站或第三方)。
- 需要设计开放型 API,便于其他第三方也能接入气象站获取数据。 WeatherData
- 提供温度、气压和湿度的接口
- 测量数据更新时,要能实时的通知给第三方
传统模式
public class AObserverPattern {
public static void main(String[] args) {
///创建接入方 currentConditions
CurrentConditions currentConditions = new CurrentConditions();
//创建 WeatherData 并将 接入方 currentConditions 传递到 WeatherData 中
WeatherData weatherData = new WeatherData(currentConditions);
// 更新天气情况
weatherData.setData(30, 150, 40);
// 天气情况变化
System.out.println("============天气情况变化=============");
weatherData.setData(40, 160, 20);
}
}
/**
* 当前 天气情况 == 可以理解成网站 展示天气状态
*/
class CurrentConditions{
private float temperature; //温度
private float pressure; // 气压
private float humidity; // 湿度
// 更新天气情况: 由 weatherData 调用,使用的推送模式 修改天气信息
public void update(float temperature,float pressure, float humidity){
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
display();
}
// 网站显示天气
private void display() {
System.out.println("===当前温度 ======" + temperature);
System.out.println("===当前气压 ======" + pressure);
System.out.println("===当前湿度 ======" + humidity);
}
}
/**
* 核心类
* 1、包含最新的天气情况信息
* 2、包含 CurrentConditions 对象
* 3、一旦天气有变化,就主动调用 CurrentConditions的 update 方法,更新网站的天气状态
*/
@Data
class WeatherData{
private float temperature; //温度
private float pressure; // 气压
private float humidity; // 湿度
private CurrentConditions currentConditions; // 湿度
public WeatherData(CurrentConditions currentConditions){
this.currentConditions = currentConditions;
}
// 当数据有更新时,调用 serData,重置气象数据 并调用 dataChange接口
public void setData(float temperature,float pressure, float humidity){
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
dataChange();
}
// 调用 网站的 更新接口 ,更新网站的信息
public void dataChange(){
currentConditions.update(getTemperature(),getPressure(),getHumidity());
}
}
解读代码
1、首先是具体的天气信息 WeatherData类
setData 方法就是更新天气情况,调用 datachange()方法,就是 调用 网站的 更新信息。
监测的天气发生变化,对应的各个网站展示的天气情况,也要随之改变发生
/**
* 核心类
* 1、包含最新的天气情况信息
* 2、包含 CurrentConditions 对象
* 3、一旦天气有变化,就主动调用 CurrentConditions的 update 方法,更新网站的天气状态
*/
@Data
class WeatherData{
private float temperature; //温度
private float pressure; // 气压
private float humidity; // 湿度
private CurrentConditions currentConditions; // 湿度
public WeatherData(CurrentConditions currentConditions){
this.currentConditions = currentConditions;
}
// 当数据有更新时,调用 serData,重置气象数据 并调用 dataChange接口
public void setData(float temperature,float pressure, float humidity){
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
dataChange();
}
// 调用 网站的 更新接口 ,更新网站的信息
public void dataChange(){
currentConditions.update(getTemperature(),getPressure(),getHumidity());
}
}
2、外部网站展示天气状态
WeatherData 更新天气状态 会对应这个外部网站的 update 方法
/**
* 当前 天气情况 == 可以理解成网站 展示天气状态
*/
class CurrentConditions{
private float temperature; //温度
private float pressure; // 气压
private float humidity; // 湿度
// 更新天气情况: 由 weatherData 调用,使用的推送模式 修改天气信息
public void update(float temperature,float pressure, float humidity){
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
display();
}
// 网站显示天气
private void display() {
System.out.println("===当前温度 ======" + temperature);
System.out.println("===当前气压 ======" + pressure);
System.out.println("===当前湿度 ======" + humidity);
}
}
4、主方法
创建
public static void main(String[] args) {
///创建接入方 currentConditions
CurrentConditions currentConditions = new CurrentConditions();
//创建 WeatherData 并将 接入方 currentConditions 传递到 WeatherData 中
WeatherData weatherData = new WeatherData(currentConditions);
// 更新天气情况
weatherData.setData(30, 150, 40);
// 天气情况变化
System.out.println("============天气情况变化=============");
weatherData.setData(40, 160, 20);
}
}
传统模式 缺点:
此时只有一个 网站接入 WeatherData ,如果再有个网站就要 写一个 和 CurrentConditions 一样的类,weatherData 里也要 再聚合一个 类对象。
违反了 opc原则
观察者模式
- 1、Subject 接口 包含注册、移除 和通知的方法
- 2、observer 观察者 接口
public class BObserverPattern {
public static void main(String[] args) {
WeatherDataInfo weatherDataInfo = new WeatherDataInfo();
Baidu baidu = new Baidu();
XinLang xinLang = new XinLang();
// 创建 观察者
weatherDataInfo.registerObserver(baidu);
weatherDataInfo.registerObserver(xinLang);
weatherDataInfo.setData(10,20,30);
System.out.println("===移除百度====");
// 移除 一个观察者
weatherDataInfo.removeObserver(baidu);
weatherDataInfo.setData(110,210,310);
}
}
/**
* 气象站
* registerObserver :注册接入气象信息的网站
* removeObserver : 移除 接入获取气象信息的网站
* notifyObserver:天气变化 通知网站 更新气象信息
*
*/
interface Subject{
// 注册
public void registerObserver(Observer o);
// 移除
public void removeObserver(Observer o);
// 通知
public void notifyObserver();
}
/**
* 观察者 接口,由观察者(接入气象信息的网站)实现
*
*/
interface Observer{
public void update(float temperature,float pressure, float humidity);
}
/**
* 观察者1号:新浪网站
*/
class XinLang implements Observer{
private float temperature; //温度
private float pressure; // 气压
private float humidity; // 湿度
// 更新天气情况: 由 weatherData 调用,使用的推送模式 修改天气信息
public void update(float temperature,float pressure, float humidity){
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
display();
}
// 网站显示天气
private void display() {
System.out.println("=XinLang==当前温度 ======" + temperature);
System.out.println("==XinLang=当前气压 ======" + pressure);
System.out.println("==XinLang=当前湿度 ======" + humidity);
}
}
class Baidu implements Observer{
private float temperature; //温度
private float pressure; // 气压
private float humidity; // 湿度
// 更新天气情况: 由 weatherData 调用,使用的推送模式 修改天气信息
public void update(float temperature,float pressure, float humidity){
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
display();
}
// 网站显示天气
private void display() {
System.out.println("=Baidu==当前温度 ======" + temperature);
System.out.println("==Baidu=当前气压 ======" + pressure);
System.out.println("==Baidu=当前湿度 ======" + humidity);
}
}
/**
* 核心类
* 1、包含最新的天气情况信息
* 2、含义 观察者集合,使用 ArrayList 管理
* 3、一旦天气有变化,就主动调用 arrayList,通知所有接入者更新网站的天气状态
*/
@Data
class WeatherDataInfo implements Subject{
private float temperature; //温度
private float pressure; // 气压
private float humidity; // 湿度
private ArrayList<Observer> observers; // 观察者 集合
public WeatherDataInfo(){
this.observers = new ArrayList<Observer>();
}
// 当数据有更新时,调用 serData,重置气象数据 并调用 dataChange接口
public void setData(float temperature,float pressure, float humidity){
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
dataChange();
}
// 调用 网站的 更新接口 ,更新网站的信息
public void dataChange(){
notifyObserver();
}
// 注册一个观察者
@Override
public void registerObserver(Observer o) {
observers.add(o);
}
// 移除一个观察者
@Override
public void removeObserver(Observer o) {
observers.remove(o);
}
// 通知所有观察者
@Override
public void notifyObserver() {
for (Observer ob : observers) {
ob.update(this.temperature,this.pressure,this.humidity);
}
}
}
解读代码
1、首先 定义一个接口 Subject
让 气象局 监测的天气信息并且对外暴露的接口来继承
/**
* 气象站
* registerObserver :注册接入气象信息的网站
* removeObserver : 移除 接入获取气象信息的网站
* notifyObserver:天气变化 通知网站 更新气象信息
*
*/
interface Subject{
// 注册
public void registerObserver(Observer o);
// 移除
public void removeObserver(Observer o);
// 通知
public void notifyObserver();
}
2、再定义一个接口 Observer
由观察者来继承,此处由 需要获取天气信息的网站 来继承
/**
* 观察者 接口,由观察者(接入气象信息的网站)实现
*
*/
interface Observer{
public void update(float temperature,float pressure, float humidity);
}
3、创建一个类对象 新浪 需要获取 实时的天气信息 为 观察者 1号
/**
* 观察者1号:新浪网站
*/
class XinLang implements Observer{
private float temperature; //温度
private float pressure; // 气压
private float humidity; // 湿度
// 更新天气情况: 由 weatherData 调用,使用的推送模式 修改天气信息
public void update(float temperature,float pressure, float humidity){
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
display();
}
// 网站显示天气
private void display() {
System.out.println("=XinLang==当前温度 ======" + temperature);
System.out.println("==XinLang=当前气压 ======" + pressure);
System.out.println("==XinLang=当前湿度 ======" + humidity);
}
}
4、核心类。气象局监测的天气信息类,
- 1、包含最新的天气情况信息
- 2、含义 观察者集合,使用 ArrayList 管理
- 3、一旦天气有变化,就主动调用 arrayList,通知所有接入者更新网站的天气状态
继承 Subject 接口 重写 注册、 移除、通知的接口 ,所有的观察者 放进 list集合中,需要通知的话,遍历集合,调用每个观察者的 update方法
/**
* 核心类
* 1、包含最新的天气情况信息
* 2、含义 观察者集合,使用 ArrayList 管理
* 3、一旦天气有变化,就主动调用 arrayList,通知所有接入者更新网站的天气状态
*/
@Data
class WeatherDataInfo implements Subject{
private float temperature; //温度
private float pressure; // 气压
private float humidity; // 湿度
private ArrayList<Observer> observers; // 观察者 集合
public WeatherDataInfo(){
this.observers = new ArrayList<Observer>();
}
// 当数据有更新时,调用 serData,重置气象数据 并调用 dataChange接口
public void setData(float temperature,float pressure, float humidity){
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
dataChange();
}
// 调用 网站的 更新接口 ,更新网站的信息
public void dataChange(){
notifyObserver();
}
// 注册一个观察者
@Override
public void registerObserver(Observer o) {
observers.add(o);
}
// 移除一个观察者
@Override
public void removeObserver(Observer o) {
observers.remove(o);
}
// 通知所有观察者
@Override
public void notifyObserver() {
for (Observer ob : observers) {
ob.update(this.temperature,this.pressure,this.humidity);
}
}
}
5、主方法
创建 实时数据WeatherDataInfo对象,
将新浪观察者放进 WeatherDataInfo 的观察者集合中,serData 更新数据 ,相应新浪也会更新数据。
创建一个 百度的 观察者,同样操作
public static void main(String[] args) {
WeatherDataInfo weatherDataInfo = new WeatherDataInfo();
Baidu baidu = new Baidu();
XinLang xinLang = new XinLang();
// 创建 观察者
weatherDataInfo.registerObserver(baidu);
weatherDataInfo.registerObserver(xinLang);
// 更新天气信息
weatherDataInfo.setData(10,20,30);
System.out.println("===移除百度====");
// 移除 一个观察者
weatherDataInfo.removeObserver(baidu);
weatherDataInfo.setData(110,210,310);
}
}