设计模式简介
本笔记是根据尚硅谷java设计模式视频学习得
设计模式七大原则
1设计模式的目的
编写软件过程中,程序员面临着来自 耦合性,内聚性以及可维护性,可扩展性,重用性,灵活性 等多方面的挑战,设计模式是为了让程序(软件),具有更好
1)代码重用性 (即:相同功能的代码,不用多次编写)
2)可读性 (即:编程规范性, 便于其他程序员的阅读和理解)
3)可扩展性 (即:当需要增加新的功能时,非常的方便,称为可维护)
4)可靠性 (即:当我们增加新的功能后,对原来的功能没有影响)
5)使程序呈现高内聚,低耦合的特性分享金句:
6)设计模式包含了面向对象的精髓,“懂了设计模式,你就懂了面向对象分析和设计(OOA/D)的精要”
7)Scott Mayers 在其巨著《Effective C++》就曾经说过:C++老手和 C++新手的区别就是前者手背上有很多伤疤
2设计模式七大原则
设计模式原则,其实就是程序员在编程时,应当遵守的原则,也是各种设计模式的基础(即:设计模式为什么这样设计的依据)
设计模式常用的七大原则有:
1)单一职责原则
2)接口隔离原则
3)依赖倒转(倒置)原则
4)里氏替换原则
5)开闭原则
6)迪米特法则
7)合成复用原则
设计模式概述
1掌握设计模式的层次
1)第 1 层:刚开始学编程不久,听说过什么是设计模式
2)第 2 层:有很长时间的编程经验,自己写了很多代码,其中用到了设计模式,但是自己却不知道
3)第 3 层:学习过了设计模式,发现自己已经在使用了,并且发现了一些新的模式挺好用的
4)第 4 层:阅读了很多别人写的源码和框架,在其中看到别人设计模式,并且能够领会设计模式的精妙和带来的好处。
5)第 5 层:代码写着写着,自己都没有意识到使用了设计模式,并且熟练的写了出来。
2设计模式介绍
1)设计模式是程序员在面对同类软件工程设计问题所总结出来的有用的经验,模式不是代码,而是某类问题的通用解决方案,设计模式(Design pattern)代表了最佳的实践。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
2)设计模式的本质提高 软件的维护性,通用性和扩展性,并降低软件的复杂度。
3)<<设计模式>> 是经典的书,作者是 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides Design(俗称 “四人组 GOF”)
4)设计模式并不局限于某种语言,java,php,c++ 都有设计模式.
3设计模式类型
设计模式分为三种类型,共 23 种
1)创建型模式:单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式。
2)结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
3)行为型模式:模版方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式(Interpreter 模式)、状态模式、策略模式、职责链模式(责任链模式)。
注意:不同的书籍上对分类和名称略有差别
详细解释代码中也有
一 单例设计模式
1单例设计模式介绍
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例, 并且该类只提供一个取得其对象实例的方法(静态方法)。
比如 Hibernate 的 SessionFactory,它充当数据存储源的代理,并负责创建 Session 对象。SessionFactory 并不是轻量级的,一般情况下,一个项目通常只需要一个 SessionFactory 就够,这是就会使用到单例模式。
2单例设计模式八种方式
单例模式有八种方式:
1)饿汉式(静态常量)
2)饿汉式(静态代码块)
3)懒汉式(线程不安全)
4)懒汉式(线程安全,同步方法)
5)懒汉式(线程安全,同步代码块)
6)双重检查
7)静态内部类
8)枚举
单例模式在JDK应用的源码分析单例模式在JDK应用的源码分析
1)我们JDK中,java.lang.Runtime就是经典的单例模式2)代码分析+Debug源码+代码说明
单例模式注意事项和细节说明
1)单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需
要频繁创建销毁的对象,使用单例模式可以提高系统性能
2)当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使
用new
3)单例模式使用的场景:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或
耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂等)
饿汉单列模式
package com.fs.sjms.single;
/**
* 饿汉单列模式
*/
public class EagerSingleton {
public static void main(String[] args) {
//测试 饿汉式(静态常量)
/* EagerSingletonStaticConstant instance = EagerSingletonStaticConstant.getInstance();
EagerSingletonStaticConstant instance2 = EagerSingletonStaticConstant.getInstance();
System.out.println(instance);
System.out.println(instance2);//输出的地址值一致,说明是同一个对象*/
//测试 饿汉式(静态代码块)
/* EagerSingletonStaticCode instance = EagerSingletonStaticCode.getInstance();
EagerSingletonStaticCode instance2 = EagerSingletonStaticCode.getInstance();
System.out.println(instance);
System.out.println(instance2);//输出的地址值一致,说明是同一个对象*/
}
}
/**
* 饿汉式(静态常量)
* 步骤:
* 1 构造私有化 (防止直接new来创建对象)
* 2 类的内部创建对象
* 3 向外暴露一个静态的公共方法
*
* 优缺点
* 优缺点说明:
* 1)优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。
* 2)缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始
* 至终从未使用过这个实例,则会造成内存的浪费
* 3)这种方式基于classloder机制避免了多线程的同步问题,不过,instance在类装载
* 时就实例化,在单例模式中大多数都是调用getlnstance方法,但是导致类装载的原因有很多种,
* 因此不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance就没有达到lazy loading的效果
* 4)结论:这种单例模式可用,可能造成内存浪费
*/
class EagerSingletonStaticConstant {
// * 2 类的内部创建对象
private final static EagerSingletonStaticConstant eagerSingletonStaticConstant = new EagerSingletonStaticConstant();
// * 1 构造私有化 (防止直接new来创建对象)
private EagerSingletonStaticConstant() {
}
// * 3 向外暴露一个静态的公共方法
public static EagerSingletonStaticConstant getInstance() {
return eagerSingletonStaticConstant;
}
}
/**
* 饿汉式(静态代码块)
*
* 优缺点说明:
* 1)这种方式和上面的方式其实类似,只不过将类实例化的过程放在了静态代码块
* 中,也是在类装载的时候,就执行静态代码块中的代码,初始化类的实例。优缺点和上面是一样的。
* 2)结论:这种单例模式可用,但是可能造成内存浪费
*/
class EagerSingletonStaticCode {
private static EagerSingletonStaticCode eagerSingletonStaticCode;
//静态代码块中创建单列对象
static {
eagerSingletonStaticCode = new EagerSingletonStaticCode();
}
// * 1 构造私有化 (防止直接new来创建对象)
private EagerSingletonStaticCode() {
}
// * 3 向外暴露一个静态的公共方法
public static EagerSingletonStaticCode getInstance() {
return eagerSingletonStaticCode;
}
}
懒汉式单列
package com.fs.sjms.single;
/**
* 懒汉式单列
*/
public class LazySingleton {
public static void main(String[] args) {
/* //测试 懒汉式 (线程不安全)
LazySingletonInsecurity instance = LazySingletonInsecurity.getInstance();
LazySingletonInsecurity instance2 = LazySingletonInsecurity.getInstance();
System.out.println(instance);
System.out.println(instance2);输出的地址值一致,说明是同一个对象*/
//但是在多线程环境下,就会导致线程不安全
/* for (int i = 0; i < 10000; i++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
//测试 懒汉式 (线程不安全)
LazySingletonInsecurity instance = LazySingletonInsecurity.getInstance();
System.out.println(instance);
*//*
com.fs.sjms.single.LazySingletonInsecurity@3b7fff9b 这个和后面的不一致
com.fs.sjms.single.LazySingletonInsecurity@2a75ae17
com.fs.sjms.single.LazySingletonInsecurity@2a75ae17
*//*
}
});
thread.start();
}*/
//测试同步synchronized 单列模式是否安全
for (int i = 0; i < 10000; i++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
//测试 懒汉式 (线程不安全)
LazySingletonSafety instance = LazySingletonSafety.getInstance();
System.out.println(instance);
/*
com.fs.sjms.single.LazySingletonSafety@5dde7148 说明是线程安全的
com.fs.sjms.single.LazySingletonSafety@5dde7148
com.fs.sjms.single.LazySingletonSafety@5dde7148
*/
}
});
thread.start();
}
}
}
/**
* 懒汉式 (线程不安全)
*
* 优缺点说明:
* 1)起到了Lazy Loading的效果,但是只能在单线程下使用。
* 2)如果在多线程下,一个线程进入了if (singleton == null)判断语句块,还未来得及
* 往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式
* 3)结论:在实际开发中,不要使用这种方式.
*/
class LazySingletonInsecurity{
private static LazySingletonInsecurity lazySingletonInsecurity;
//构造方法私有化
private LazySingletonInsecurity(){}
//提供一个静态的公有方法,当使用到该方法时候。才去创建
public static LazySingletonInsecurity getInstance(){
if (lazySingletonInsecurity == null){
lazySingletonInsecurity = new LazySingletonInsecurity();
}
return lazySingletonInsecurity;
}
}
/**
* 懒汉式 (线程安全,同步方法)
*
* 优缺点说明:
* 1)解决了线程不安全问题
* 2)效率太低了,每个线程在想获得类的实例时候,执行getInstance()方法都要进行
* 同步。而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接return就行了。方法进行同步效率太低
* 3)结论:在实际开发中,不推荐使用这种方式
*/
class LazySingletonSafety{
private static LazySingletonSafety lazySingletonSafety;
//构造方法私有化
private LazySingletonSafety(){}
//提供一个静态的公有方法,当使用到该方法时候。才去创建
//synchronized 假如同步关键字,解决线程安全问题
public static synchronized LazySingletonSafety getInstance(){
if (lazySingletonSafety == null){
lazySingletonSafety = new LazySingletonSafety();
}
return lazySingletonSafety;
}
}
/**
* 懒汉式 (线程不安全,同步代码块方法,注意:不能解决线程安全问题)
*
* 优缺点说明:
* 1)这种方式,本意是想对第四种实现方式的改进,因为前面同步方法效率太低,
* 改为同步产生实例化的的代码块
* 2)但是这种同步并不能起到线程同步的作用。跟第3种实现方式遇到的情形一
* 致,假如一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例
* 3)结论:在实际开发中,不能使用这种方式
*/
class LazySingletonSafetySyncBlock{
private static LazySingletonSafetySyncBlock lazySingletonSafetySyncBlock;
//构造方法私有化
private LazySingletonSafetySyncBlock(){}
//提供一个静态的公有方法,当使用到该方法时候。才去创建
public static LazySingletonSafetySyncBlock getInstance(){
if (lazySingletonSafetySyncBlock == null){
//添加同步代码块,不能解决线程安全问题
//注意,这样不能解决线程安全问题,但是这种同步并不能起到线程同步的作用。跟第3种实现方式遇到的情形一
//致,假如一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,
// 另一个线程也通过了这个判断语句,这时便会产生多个实例
synchronized (LazySingletonSafetySyncBlock.class){
lazySingletonSafetySyncBlock = new LazySingletonSafetySyncBlock();
}
}
return lazySingletonSafetySyncBlock;
}
}
双重检查
package com.fs.sjms.single;
/**
* 双重检查
*/
public class DuplicationCheck {
public static void main(String[] args) {
//测试同步synchronized 单列模式是否安全
for (int i = 0; i < 10000; i++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
//测试 懒汉式 双重检查
LazySingletonDuplicationCheck instance = LazySingletonDuplicationCheck.getInstance();
System.out.println(instance);
}
});
thread.start();
}
}
}
/**
* 双重检查
*
* 优缺点说明:
* 1) Double-Check概念是多线程开发中常使用到的,如代码中所示,我们进行了两
* 次if (singleton == null)检查,这样就可以保证线程安全了。
* 2)这样,实例化代码只用执行一次,后面再次访问时,判断if (singleton mm null),
* 直接return实例化对象,也避免的反复进行方法同步.
* 3)线程安全;延迟加载;效率较高
* 4)结论:在实际开发中,推荐使用这种单例设计模式
*/
class LazySingletonDuplicationCheck{
private static volatile LazySingletonDuplicationCheck lazySingletonDuplicationCheck;
//构造方法私有化
private LazySingletonDuplicationCheck(){}
//提供一个静态的公有方法,当使用到该方法时候。才去创建
//双重检查
public static LazySingletonDuplicationCheck getInstance(){
if (lazySingletonDuplicationCheck == null){
synchronized (LazySingletonDuplicationCheck.class){
if (lazySingletonDuplicationCheck == null){
lazySingletonDuplicationCheck = new LazySingletonDuplicationCheck();
}
}
}
return lazySingletonDuplicationCheck;
}
}
静态内部类的方式
package com.fs.sjms.single;
/**
* 懒加载单列 静态内部类的方式
*/
public class StaticInnerClass {
public static void main(String[] args) {
//测试静态内部类 单列模式是否安全
for (int i = 0; i < 10000; i++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
//测试 懒汉式 双重检查
LazySingletonStaticInnerClass instance = LazySingletonStaticInnerClass.getInstance();
System.out.println(instance);
}
});
thread.start();
}
}
}
/**
* 懒加载单列 静态内部类的方式
*
* 优缺点说明I
* 1)这种方式采用了类装载的机制来保证初始化实例时只有一个线程。
* 2)静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化
* 时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。
* 3)类的静态属性只会在第一次加载类的时候初始化,所以在这里,JⅣM帮助我们
* 保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
* 4)优点:避免了线程不安全,利用静态内部类特点实现延迟加载,效率高5)结论:推荐使用.
*/
class LazySingletonStaticInnerClass{
//构造方法私有化
private LazySingletonStaticInnerClass(){}
//静态内部类方法
// * 类的静态属性只会在第一次加载类的时候初始化,所以在这里,JⅣM帮助我们
// * 保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
private static class LazySingletonStaticInnerClassClass{
private static LazySingletonStaticInnerClass lazySingletonStaticInnerClass=new LazySingletonStaticInnerClass();
}
public static LazySingletonStaticInnerClass getInstance(){
return LazySingletonStaticInnerClassClass.lazySingletonStaticInnerClass;
}
}
枚举实现单列模式
package com.fs.sjms.single;
/**
* 枚举实现单列模式
*/
public class EnumSingleton {
public static void main(String[] args) {
SingletonEnumClass instance = SingletonEnumClass.INSTANCE;
SingletonEnumClass instance2 = SingletonEnumClass.INSTANCE;
System.out.println(instance);
System.out.println(instance2);
System.out.println(instance == instance2);
System.out.println(instance.hashCode());
System.out.println(instance2.hashCode());
/*
可以发现输出的是一样的,说明也能完成
INSTANCE
INSTANCE
true
21685669
21685669
*/
}
}
/**
* 枚举实现单列模式
*
*优缺点说明:
* 1)这借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。
* 2)这种方式是Effective Java作者Josh Bloch提倡的方式3)结论:推荐使用
*/
enum SingletonEnumClass{
INSTANCE;
public void testMethod(){
System.out.println("我也可以实现单列模式");
}
}
二 工厂模式
1)工厂模式的意义
将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到和主项目的依赖关系的解耦。从而提高项目的扩展和维护性。
2)三种工厂模式 (简单工厂模式、工厂方法模式、抽象工厂模式)
3)设计模式的依赖抽象原则
创建对象实例时,不要直接 new 类, 而是把这个 new 类的动作放在一个工厂的方法中,并返回。有的书上说, 变量不要直接持有具体类的引用。
不要让类继承具体类,而是继承抽象类或者是实现 interface(接口)
不要覆盖基类中已经实现的方法。
简单(静态)工厂模式
package com.fs.sjms.factory;
/**
* 简单(静态)工厂模式
*
* 基本介绍
* 1)简单工厂模式是属于创建型模式,是工厂模式的一种。简单工厂模式是由一
* 个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式
* 2)简单工厂模式:定义了一个创建对象的类,由这个类来封装实例化对象的行为(代码)
* 3)在软件开发中,当我们会用到大量的创建某种、某类或者某批对象时,就会使用到工厂模式.
*
* 看一个具体的需求
* 看一个披萨的项目:要便于披萨种类的扩展,要便于维护
* 1)披萨的种类很多(比如GreekPizza、CheesePizza、 PepperPizza等)
* 2)披萨的制作有prepare,bake, cut, box
* 3)完成披萨店订购功能。
*/
public class SimpleFactoryDemo {
public static void main(String[] args) {
//测试简单工厂
SimpleFactory.getPizza("GreekPizza");
SimpleFactory.getPizza("CheesePizza");
SimpleFactory.getPizza("PepperPizza");
SimpleFactory.getPizza("没有的pizza");
/*
输入结果
GreekPizza---prepare
GreekPizza---bake
GreekPizza---cut
GreekPizza---box
请尽情享用吧····
CheesePizza---prepare
CheesePizza---bake
CheesePizza---cut
CheesePizza---box
请尽情享用吧····
PepperPizza---prepare
PepperPizza---bake
PepperPizza---cut
PepperPizza---box
请尽情享用吧····
当前pizza还未上线,请尽情期待哟····
Process finished with exit code 0
*/
}
}
/**
* 简单工厂类
*/
class SimpleFactory{
//用户传递想要的pizza就生产他需要的
public static Pizza getPizza(String pizzaName){
Pizza pizza = OrderPizza.creatPizza(pizzaName);
if (pizza!=null){
//说明用户想要的pizza工厂中有
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
System.out.println("请尽情享用吧····");
}else {
//说明没有
System.out.println("当前pizza还未上线,请尽情期待哟····");
}
return pizza;
}
}
/**
* 制作pizza的店家
*/
class OrderPizza{
//根据用户需要什么披萨而产出什么披萨
public static Pizza creatPizza(String pizzaName){
switch (pizzaName){
case "GreekPizza":
return new GreekPizza();
case "CheesePizza":
return new CheesePizza();
case "PepperPizza":
return new PepperPizza();
}
return null;
}
}
/**
* 披萨抽象类
*/
abstract class Pizza{
//制作pizza的过程
abstract void prepare();
abstract void bake();
abstract void cut();
abstract void box();
}
/**
* 三种pizz对象
*/
class GreekPizza extends Pizza{
@Override
void prepare() {
System.out.println("GreekPizza---prepare");
}
@Override
void bake() {
System.out.println("GreekPizza---bake");
}
@Override
void cut() {
System.out.println("GreekPizza---cut");
}
@Override
void box() {
System.out.println("GreekPizza---box");
}
}
class CheesePizza extends Pizza{
@Override
void prepare() {
System.out.println("CheesePizza---prepare");
}
@Override
void bake() {
System.out.println("CheesePizza---bake");
}
@Override
void cut() {
System.out.println("CheesePizza---cut");
}
@Override
void box() {
System.out.println("CheesePizza---box");
}
}
class PepperPizza extends Pizza{
@Override
void prepare() {
System.out.println("PepperPizza---prepare");
}
@Override
void bake() {
System.out.println("PepperPizza---bake");
}
@Override
void cut() {
System.out.println("PepperPizza---cut");
}
@Override
void box() {
System.out.println("PepperPizza---box");
}
}
工厂方法模式
package com.fs.sjms.factory;
/**
* 工厂方法模式
*
* 看一个新的需求
* 披萨项目新的需求:客户在点披萨时,可以点不同口味的披萨,比如北京的奶酪pizza、北京的胡椒pizza或者是伦敦的奶酪pizza、伦敦的胡椒pizza。
* 思路1
* 使用简单工厂模式,创建不同的简单工厂类,比如BJPizzaSimpleFactory、
* LDPizzaSimpleFactory 等等.从当前这个案例来说,也是可以的,但是考虑到项目的规模,以及软件的可维护性、可扩展性并不是特别好
* 思路2
* 使用工厂方法模式
* 工厂方法模式工厂方法模式介绍
* 工厂方法模式设计方案:将披萨项目的实例化功能抽象成抽象方法,在不同的口味点餐子类中具体实现。
* 工厂方法模式:定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类。
*
*/
public class FactoryMethodDemo {
public static void main(String[] args) {
//测试 使用BJCheesePizzaMethod
PizzaMethod bjCheesePizzaMethod = new BJOrderPizzaMethod().getPizza("CheesePizza");
bjCheesePizzaMethod.prepare();
bjCheesePizzaMethod.bake();
bjCheesePizzaMethod.cut();
bjCheesePizzaMethod.box();
}
}
/**
* 制作pizza的店家
*/
abstract class OrderPizzaMethod{
//制作不同地方的披萨抽象
public abstract PizzaMethod getPizza(String pizzaName);
}
class BJOrderPizzaMethod extends OrderPizzaMethod{
//根据用户需要什么披萨而产出什么披萨
@Override
public PizzaMethod getPizza(String pizzaName){
switch (pizzaName){
case "CheesePizza":
return new BJCheesePizzaMethod();
case "PepperPizza":
return new BJPepperPizzaMethod();
}
return null;
}
}
class LDOrderPizzaMethod extends OrderPizzaMethod{
//根据用户需要什么披萨而产出什么披萨
@Override
public PizzaMethod getPizza(String pizzaName){
switch (pizzaName){
case "PepperPizza":
return new LDPepperPizzaMethod();
case "CheesePizza":
return new LDCheesePizzaMethod();
}
return null;
}
}
/**
* 披萨抽象类
*/
abstract class PizzaMethod{
//制作pizza的过程
abstract void prepare();
abstract void bake();
abstract void cut();
abstract void box();
}
/**
* 比如北京的奶酪pizza、北京的胡椒pizza或者是伦敦的奶酪pizza、伦敦的胡椒pizza。
*/
class BJCheesePizzaMethod extends PizzaMethod{
@Override
void prepare() {
System.out.println("BJCheesePizzaMethod---prepare");
}
@Override
void bake() {
System.out.println("BJCheesePizzaMethod---bake");
}
@Override
void cut() {
System.out.println("BJCheesePizzaMethod---cut");
}
@Override
void box() {
System.out.println("BJCheesePizzaMethod---box");
}
}
class LDCheesePizzaMethod extends PizzaMethod{
@Override
void prepare() {
System.out.println("LDCheesePizzaMethod---prepare");
}
@Override
void bake() {
System.out.println("LDCheesePizzaMethod---bake");
}
@Override
void cut() {
System.out.println("LDCheesePizzaMethod---cut");
}
@Override
void box() {
System.out.println("LDCheesePizzaMethod---box");
}
}
class BJPepperPizzaMethod extends PizzaMethod{
@Override
void prepare() {
System.out.println("BJPepperPizzaMethod---prepare");
}
@Override
void bake() {
System.out.println("BJPepperPizzaMethod---bake");
}
@Override
void cut() {
System.out.println("BJPepperPizzaMethod---cut");
}
@Override
void box() {
System.out.println("BJPepperPizzaMethod---box");
}
}
class LDPepperPizzaMethod extends PizzaMethod{
@Override
void prepare() {
System.out.println("LDPepperPizzaMethod---prepare");
}
@Override
void bake() {
System.out.println("LDPepperPizzaMethod---bake");
}
@Override
void cut() {
System.out.println("LDPepperPizzaMethod---cut");
}
@Override
void box() {
System.out.println("LDPepperPizzaMethod---box");
}
}
抽象工厂模式
package com.fs.sjms.factory;
/**
*
* 抽象工厂模式基本介绍
*
* 1))抽象工厂模式:定义了一个interface用于创建相关或有依赖关系的对象簇,而无需指明具体的类
* 2)抽象工厂模式可以将简单工厂模式和工厂方法模式进行整合。
* 3)从设计层面看,抽象工厂模式就是对简单工厂模式的改进(或者称为进一步的抽象)。
* 4)将工厂抽象成两层,AbsFactory(抽象工厂)和具体实现的工厂子类。程序员可以根据创建对象类型使用对应的工厂子类。这样将单个的简单工厂类变成了工厂簇,更利于代码的维护和扩展。
*
*/
public class AbstractFactoryDemo {
public static void main(String[] args) {
//测试抽象工厂 需要北京的奶酪口味的
PizzaAbstract cheesePizza = OrderPizzaAbstract.setFactory(new BJAbstractFactory(), "CheesePizza");
cheesePizza.prepare();
cheesePizza.bake();
cheesePizza.box();
cheesePizza.cut();
//测试抽象工厂 需要伦敦的奶酪口味的
PizzaAbstract LDcheesePizza = OrderPizzaAbstract.setFactory(new LDAbstractFactory(), "CheesePizza");
LDcheesePizza.prepare();
LDcheesePizza.bake();
LDcheesePizza.box();
LDcheesePizza.cut();
}
}
/**
* 制作pizza的抽象工厂
*/
interface AbstractFactory{
//让工厂子类具体实现
PizzaAbstract getPizza(String pizzaName);
}
class BJAbstractFactory implements AbstractFactory{
//根据用户需要什么披萨而产出什么披萨
@Override
public PizzaAbstract getPizza(String pizzaName){
switch (pizzaName){
case "CheesePizza":
return new BJCheesePizzaAbstract();
case "PepperPizza":
return new BJPepperPizzaAbstract();
}
return null;
}
}
class LDAbstractFactory implements AbstractFactory{
//根据用户需要什么披萨而产出什么披萨
@Override
public PizzaAbstract getPizza(String pizzaName){
switch (pizzaName){
case "PepperPizza":
return new LDPepperPizzaAbstract();
case "CheesePizza":
return new LDCheesePizzaAbstract();
}
return null;
}
}
/**
* 生产pizza的店家
*/
class OrderPizzaAbstract{
public static PizzaAbstract setFactory(AbstractFactory abstractFactory,String pizzaName){
return abstractFactory.getPizza(pizzaName);
}
}
/**
* 披萨抽象类
*/
abstract class PizzaAbstract{
//制作pizza的过程
abstract void prepare();
abstract void bake();
abstract void cut();
abstract void box();
}
/**
* 比如北京的奶酪pizza、北京的胡椒pizza或者是伦敦的奶酪pizza、伦敦的胡椒pizza。
*/
class BJCheesePizzaAbstract extends PizzaAbstract{
@Override
void prepare() {
System.out.println("BJCheesePizzaAbstract---prepare");
}
@Override
void bake() {
System.out.println("BJCheesePizzaAbstract---bake");
}
@Override
void cut() {
System.out.println("BJCheesePizzaAbstract---cut");
}
@Override
void box() {
System.out.println("BJCheesePizzaAbstract---box");
}
}
class LDCheesePizzaAbstract extends PizzaAbstract{
@Override
void prepare() {
System.out.println("LDCheesePizzaAbstract---prepare");
}
@Override
void bake() {
System.out.println("LDCheesePizzaAbstract---bake");
}
@Override
void cut() {
System.out.println("LDCheesePizzaAbstract---cut");
}
@Override
void box() {
System.out.println("LDCheesePizzaAbstract---box");
}
}
class BJPepperPizzaAbstract extends PizzaAbstract{
@Override
void prepare() {
System.out.println("BJPepperPizzaAbstract---prepare");
}
@Override
void bake() {
System.out.println("BJPepperPizzaAbstract---bake");
}
@Override
void cut() {
System.out.println("BJPepperPizzaAbstract---cut");
}
@Override
void box() {
System.out.println("BJPepperPizzaAbstract---box");
}
}
class LDPepperPizzaAbstract extends PizzaAbstract{
@Override
void prepare() {
System.out.println("LDPepperPizzaAbstract---prepare");
}
@Override
void bake() {
System.out.println("LDPepperPizzaAbstract---bake");
}
@Override
void cut() {
System.out.println("LDPepperPizzaAbstract---cut");
}
@Override
void box() {
System.out.println("LDPepperPizzaAbstract---box");
}
}
三 原型模式
原型模式
克隆羊问题
现在有一只羊 tom,姓名为: tom, 年龄为:1,颜色为:白色,请编写程序创建和 tom 羊 属性完全相同的 10
只羊。
传统方式解决克隆羊问题
传统的方式的优缺点
1)优点是比较好理解,简单易操作。
2)在创建新的对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂时,效率较低
3)总是需要重新初始化对象,而不是动态地获得对象运行时的状态,不够灵活
4)改进的思路分析
思路:Java中object类是所有类的根类,Object类提供了一个clone()方法,该方法可以将一个Java对象复制一份,但是需要实现clone的Java类必须要实现一个接口Cloneable,该接口表示该类能够复制且具有复制的能力=>原型模式
●原型模式-基本介绍基本介绍
1)原型模式(Prototype模式)是指:用原型实例指定创建对象的种类,并且通过拷
贝这些原型,创建新的对象
2)原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,
无需知道如何创建的细节
3)工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建
的对象通过请求原型对象拷贝它们自己来实施创建,即对象.clone()
4)形象的理解:孙大圣拔出猴毛,变出其它孙大圣
5)原型模式只是属性的复制,生产出的对象不是原来的对象,是新对象。在spring源码中
若在xml中配置bean的时候socpe=“prototype”的话,从ioc获取的对象就是原型模式,
新建对象复制属性获取的,而不是单列模式
●深入讨论-浅拷贝和深拷贝
浅拷贝的介绍
1)对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。
2)对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类
的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值
3)前面我们克隆羊就是浅拷贝
4)浅拷贝是使用默认的clone()方法来实现
sheep = (Sheep) super.clone();
深拷贝基本介绍
1)复制对象的所有基本数据类型的成员变量值
2)为所有引用数据类型的成员变量中请存储空间,并复制每个引用数据类型成员变
量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象进行拷贝
3)深拷贝实现方式1:重写clone方法来实现深拷贝
4)深拷贝实现方式2:通过对象序列化实现深拷贝
原型模式的注意事项和细节
1)创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率
2)不用重新初始化对象,而是动态地获得对象运行时的状态
3)如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码
4)在实现深克隆的时候可能需要比较复杂的代码
5)缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了 ocp 原则,这点请同学们注意.
原型模式解决克隆羊问题
package com.fs.sjms.prototype;
/**
* 原型模式解决克隆羊问题
*
* 现在有一只羊 tom,姓名为: tom, 年龄为:1,颜色为:白色,
* 请编写程序创建和 tom 羊 属性完全相同的 5只羊。
*/
public class PrototypeClone{
public static void main(String[] args) {
//测试原型模式克隆羊
Sheep sheep = new Sheep("tom", 1, "白色");
Sheep clone = (Sheep) sheep.clone();
Sheep clone2 = (Sheep) sheep.clone();
Sheep clone3 = (Sheep) sheep.clone();
Sheep clone4 = (Sheep) sheep.clone();
Sheep clone5 = (Sheep) sheep.clone();
Sheep clone6 = (Sheep) sheep.clone();
System.out.println(clone);
System.out.println(clone2);
System.out.println(clone3);
System.out.println(clone4);
System.out.println(clone5);
System.out.println(clone6);
}
}
/**
* 羊实体类,要使用到原型模式,需要实现Cloneable接口,从写clone()方法
*/
class Sheep implements Cloneable{
private String name;
private Integer age;
private String colour;
public Sheep(String name, Integer age, String colour) {
this.name = name;
this.age = age;
this.colour = colour;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getColour() {
return colour;
}
public void setColour(String colour) {
this.colour = colour;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
", colour='" + colour + '\'' +
'}';
}
/**
* 重写clone方法
*/
@Override
protected Object clone() {
Sheep clone = null;
try {
clone = (Sheep)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
浅拷贝
package com.fs.sjms.prototype.shallow;
/**
* 浅拷贝
*
* 浅拷贝的介绍
*
* 1)对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。
* 2)对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值
*
* 现在有一只羊 tom,姓名为: tom, 年龄为:1,颜色为:白色,他的朋友xiaoming羊
* 请编写程序创建和 tom 羊 属性完全相同的 5只羊。
*/
public class PrototypeShallowClone{
public static void main(String[] args) {
//测试原型模式克隆羊
Sheep sheep = new Sheep("tom", 1, "白色",new Sheep("xiaoming",2,"黑色",null));
Sheep clone = (Sheep) sheep.clone();
Sheep clone2 = (Sheep) sheep.clone();
Sheep clone3 = (Sheep) sheep.clone();
Sheep clone4 = (Sheep) sheep.clone();
Sheep clone5 = (Sheep) sheep.clone();
Sheep clone6 = (Sheep) sheep.clone();
System.out.println(clone+"clone hash:"+clone.hashCode()+"clone de friend:"+clone.getFriend().hashCode());
System.out.println(clone2+"clone2 hash:"+clone2.hashCode()+"clone2 de friend:"+clone2.getFriend().hashCode());
System.out.println(clone3+"clone3 hash:"+clone3.hashCode()+"clone3 de friend:"+clone3.getFriend().hashCode());
System.out.println(clone4+"clone4 hash:"+clone4.hashCode()+"clone4 de friend:"+clone4.getFriend().hashCode());
System.out.println(clone5+"clone5 hash:"+clone5.hashCode()+"clone5 de friend:"+clone5.getFriend().hashCode());
System.out.println(clone6+"clone6 hash:"+clone6.hashCode()+"clone6 de friend:"+clone6.getFriend().hashCode());
/*
Sheep{name='tom', age=1, colour='白色', friend=Sheep{name='xiaoming', age=2, colour='黑色', friend=null}}clone hash:21685669clone de friend:2133927002
Sheep{name='tom', age=1, colour='白色', friend=Sheep{name='xiaoming', age=2, colour='黑色', friend=null}}clone2 hash:1836019240clone2 de friend:2133927002
Sheep{name='tom', age=1, colour='白色', friend=Sheep{name='xiaoming', age=2, colour='黑色', friend=null}}clone3 hash:325040804clone3 de friend:2133927002
Sheep{name='tom', age=1, colour='白色', friend=Sheep{name='xiaoming', age=2, colour='黑色', friend=null}}clone4 hash:1173230247clone4 de friend:2133927002
Sheep{name='tom', age=1, colour='白色', friend=Sheep{name='xiaoming', age=2, colour='黑色', friend=null}}clone5 hash:856419764clone5 de friend:2133927002
Sheep{name='tom', age=1, colour='白色', friend=Sheep{name='xiaoming', age=2, colour='黑色', friend=null}}clone6 hash:621009875clone6 de friend:2133927002
由输出结果得知,复制的tom羊是新建的对象复制原有对象的属性值,而tom的朋友对象是引用的同一个对象,这样的拷贝叫浅拷贝
*/
}
}
/**
* 羊实体类,要使用到原型模式,需要实现Cloneable接口,从写clone()方法
*/
class Sheep implements Cloneable{
private String name;
private Integer age;
private String colour;
//若我们的属性有实体类型呢?clone复制的是值?还是地址引用?
private Sheep friend;
public Sheep(String name, Integer age, String colour, Sheep friend) {
this.name = name;
this.age = age;
this.colour = colour;
this.friend = friend;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getColour() {
return colour;
}
public void setColour(String colour) {
this.colour = colour;
}
public Sheep getFriend() {
return friend;
}
public void setFriend(Sheep friend) {
this.friend = friend;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
", colour='" + colour + '\'' +
", friend=" + friend +
'}';
}
/**
* 重写clone方法
*/
@Override
protected Object clone() {
Sheep clone = null;
try {
clone = (Sheep)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
深拷贝
package com.fs.sjms.prototype.deep;
import java.io.*;
/**
* 深拷贝
* <p>
* 深拷贝基本介绍
* <p>
* 1)复制对象的所有基本数据类型的成员变量值
* 2)为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象(包括对象的引用类型)进行拷贝
* <p>
* 3)深拷贝实现方式 1:重写 clone 方法来实现深拷贝
* 4)深拷贝实现方式 2:通过对象序列化实现深拷贝(推荐)
* <p>
* 现在有一只羊 tom,姓名为: tom, 年龄为:1,颜色为:白色,他的小伙伴dog
* 请编写程序创建和 tom 羊 属性完全相同的 5只羊。
*/
public class PrototypeDeepClone {
public static void main(String[] args) {
//测试 深拷贝实现方式 1:重写 clone 方法来实现深拷贝
/* Sheep sheep = new Sheep("tom", 1, "白色",new Dog("dog"));
Sheep clone = (Sheep) sheep.clone();
Sheep clone2 = (Sheep) sheep.clone();
Sheep clone3 = (Sheep) sheep.clone();
Sheep clone4 = (Sheep) sheep.clone();
Sheep clone5 = (Sheep) sheep.clone();
Sheep clone6 = (Sheep) sheep.clone();
System.out.println(clone+"clone hash:"+clone.hashCode()+",clone de friend:"+clone.getFriend().hashCode());
System.out.println(clone2+"clone2 hash:"+clone2.hashCode()+",clone2 de friend:"+clone2.getFriend().hashCode());
System.out.println(clone3+"clone3 hash:"+clone3.hashCode()+",clone3 de friend:"+clone3.getFriend().hashCode());
System.out.println(clone4+"clone4 hash:"+clone4.hashCode()+",clone4 de friend:"+clone4.getFriend().hashCode());
System.out.println(clone5+"clone5 hash:"+clone5.hashCode()+",clone5 de friend:"+clone5.getFriend().hashCode());
System.out.println(clone6+"clone6 hash:"+clone6.hashCode()+",clone6 de friend:"+clone6.getFriend().hashCode());*/
/*
Sheep{name='tom', age=1, colour='白色', friend=Dog{name='dog'}}clone hash:21685669,clone de friend:2133927002
Sheep{name='tom', age=1, colour='白色', friend=Dog{name='dog'}}clone2 hash:1836019240,clone2 de friend:325040804
Sheep{name='tom', age=1, colour='白色', friend=Dog{name='dog'}}clone3 hash:1173230247,clone3 de friend:856419764
Sheep{name='tom', age=1, colour='白色', friend=Dog{name='dog'}}clone4 hash:621009875,clone4 de friend:1265094477
Sheep{name='tom', age=1, colour='白色', friend=Dog{name='dog'}}clone5 hash:2125039532,clone5 de friend:312714112
Sheep{name='tom', age=1, colour='白色', friend=Dog{name='dog'}}clone6 hash:692404036,clone6 de friend:1554874502
由输出得,都是克隆生成的新对象,这就叫深拷贝
*/
System.out.println("--------------------------------------------------------------------------------------------");
//测试 深拷贝实现方式 2:通过对象序列化实现深拷贝(推荐)
Sheep sheep = new Sheep("tom", 1, "白色", new Dog("dog"));
Sheep clone = sheep.deepClone();
Sheep clone2 = sheep.deepClone();
Sheep clone3 = sheep.deepClone();
Sheep clone4 = sheep.deepClone();
Sheep clone5 = sheep.deepClone();
Sheep clone6 = sheep.deepClone();
System.out.println(clone + "clone hash:" + clone.hashCode() + ",clone de friend:" + clone.getFriend().hashCode());
System.out.println(clone2 + "clone2 hash:" + clone2.hashCode() + ",clone2 de friend:" + clone2.getFriend().hashCode());
System.out.println(clone3 + "clone3 hash:" + clone3.hashCode() + ",clone3 de friend:" + clone3.getFriend().hashCode());
System.out.println(clone4 + "clone4 hash:" + clone4.hashCode() + ",clone4 de friend:" + clone4.getFriend().hashCode());
System.out.println(clone5 + "clone5 hash:" + clone5.hashCode() + ",clone5 de friend:" + clone5.getFriend().hashCode());
System.out.println(clone6 + "clone6 hash:" + clone6.hashCode() + ",clone6 de friend:" + clone6.getFriend().hashCode());
/*
Sheep{name='tom', age=1, colour='白色', friend=Dog{name='dog'}}clone hash:1867750575,clone de friend:2046562095
Sheep{name='tom', age=1, colour='白色', friend=Dog{name='dog'}}clone2 hash:1342443276,clone2 de friend:769287236
Sheep{name='tom', age=1, colour='白色', friend=Dog{name='dog'}}clone3 hash:1587487668,clone3 de friend:1199823423
Sheep{name='tom', age=1, colour='白色', friend=Dog{name='dog'}}clone4 hash:932172204,clone4 de friend:1225358173
Sheep{name='tom', age=1, colour='白色', friend=Dog{name='dog'}}clone5 hash:1848402763,clone5 de friend:933699219
Sheep{name='tom', age=1, colour='白色', friend=Dog{name='dog'}}clone6 hash:2121055098,clone6 de friend:2084435065
同样地址值不同,得知为深拷贝
*/
}
}
/**
* 羊实体类,要使用到原型模式,需要实现Cloneable接口,从写clone()方法
*/
class Sheep implements Serializable,Cloneable {
private String name;
private Integer age;
private String colour;
//若我们的属性有实体类型呢?clone复制的是值?还是地址引用?
private Dog friend;
public Sheep(String name, Integer age, String colour, Dog friend) {
this.name = name;
this.age = age;
this.colour = colour;
this.friend = friend;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getColour() {
return colour;
}
public void setColour(String colour) {
this.colour = colour;
}
public Dog getFriend() {
return friend;
}
public void setFriend(Dog friend) {
this.friend = friend;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
", colour='" + colour + '\'' +
", friend=" + friend +
'}';
}
/**
* 重写clone方法
* 3)深拷贝实现方式 1:重写 clone 方法来实现深拷贝
*/
@Override
protected Object clone() {
Sheep clone = null;
try {
//先对基本属性进行克隆
clone = (Sheep) super.clone();
//在对实体属性进克隆
clone.setFriend((Dog) clone.getFriend().clone());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
/**
* 深拷贝实现方式 2:通过对象序列化实现深拷贝(推荐)
*/
public Sheep 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);
//将当前对象从流中读出来
Sheep o = (Sheep) ois.readObject();
return o;
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
//关流
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 羊的小伙伴dog
* <p>
* 要实现深拷贝需要序列化与实现Cloneable接口
*/
class Dog implements Serializable, Cloneable {
private static final long serialVersionUID = 1L;
private String name;
public Dog(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
'}';
}
//重写clone方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}