设计模式(01)
Posted 爱德华的奇妙生活
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式(01)相关的知识,希望对你有一定的参考价值。
1
什么是设计模式
设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性以及类的关联关系和组合关系的充分理解。
2
设计模式的基本要素
模式名称
问题
解决方案
效果
3
GoF 23(思维、态度、进步
创建型模式:
描述如何去创建一个对象,让对象的创建与使用分离。
单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式
结构型模式:
描述如何将类与对象按照某种布局组成更大的结构。
适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式
行为型模式:
描述类或者对象之间如何相互协作,共同完成单个对象无法完成的任务,主要是分配一些职责。
模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式
4
OOP七大原则
开闭原则:对扩展开放、对修改关闭
里氏替换原则:继承必须确保超类所拥有的的性质在子类中仍然成立(子类可以扩展父类的功能,但是尽量不要更改父类的功能,在子类继承父类的时候,尽量添加新的方法、新的功能,而不是将父类的方法改掉)
单一职责原则:控制类的粒度大小、将对象解耦、提高其内聚性(一个对象不应该承担太多的职责,否则会出现这样的缺点:当客户端需要该对象的某一个职责的时候也即某一块功能的时候,我们不得不将它其他的东西全部包含起来,从而造成代码的冗余。说白了就是一个方法尽可能干好一件事情,也就是原子性)
接口分离原则:要为各个类建立它们需要的专用接口
依赖倒置原则:要面向接口编程,不要面向实现编程
迪米特法则:只与你的直接朋友交谈,不跟“陌生人”说话(弊端,会增加很多的中介类,提高系统的复杂性)
合成复用原则:尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现
5
单例模式
5.1
核心作用
保证一个类只有一个实例,并且提供一个访问该类里的全局访问点
5.2
常见场景
Windows的任务管理器
Windows的回收站
项目中,读取配置文件的类,一般也只有一个对象,没必要每次都去new对象读取
网站的计数器一般也会采用单例模式,可以保证同步
数据库连接池的设计一般也是单例模式
在servlet编程中,每个Servlet也是单例的
在Spring中,每个Bean默认就是单例的
......
5.3
饿汉式
public class Hungry {
//饿汉式单例
private byte[] data1 = new byte[1024*1024];
private byte[] data1 = new byte[1024*1024];
private byte[] data1 = new byte[1024*1024];
private byte[] data1 = new byte[1024*1024];
public Hungry(){
}
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance(){
return HUNGRY;
}
}
}
5.4
懒汉式单例
public class LazyMan{
private LazyMan(){
System.out.println(Thread.currentThread().getName()+"ok");
}
private volatile static LazyMan lazyMan; //加上volatile避免指令重排
/* 多线程并发修改,加锁,双重检测锁模式的懒汉式单例 DCL懒汉式
public static LazyMan getInstance(){
if(lazyMan==null){
synchronized(LazyMan.class){
if(lazyMan==null){
lazyMan = new LazyMan(); //不是一个原子性操作
}}
} */
/*
1.分配内存空间
2.执行构造方法
3.把这个对象指向这个空间
*/
if(lazyMan==null){
lazyMan = new LazyMan();
}
return lazyMan;
}
//单线程可以,多线程会出现问题
//多线程并发
public static void main(String[] args){
for(int i = 0;i<10;i++){
new Thread(()->{
LazyMan.getInstance();
}).start();
}
}
}
5.5
静态内部类实现
public class Holder{
private Holder(){
}
public static Holder getInstance(){
return InnerClass.HOLDER;
}
public static class InnerClass{
private static final Holder HOLDER = new Holder();
}
}
5.6
反射
public static void main(String[] args) throws Exception{
LazyMan instance = LazyMan.getInstance();
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null)
declaredConstructor.setAccessible(true);//无视私有构造器,可以通过反射来创建对象
LazyMan instance2 = declaredConstructor.newInstance();
}
反射可以破坏单例
解决方法
public class LazyMan{
private LazyMan(){
synchronized(LazyMan.class){
if(lazyMan!=null){
throw new RuntimeException("不要试图用反射破坏异常")
}
}
}
private volatile static LazyMan lazyMan; //加上volatile避免指令重排
//多线程并发修改,加锁,双重检测锁模式的懒汉式单例 DCL懒汉式
public static LazyMan getInstance(){
if(lazyMan==null){
synchronized(LazyMan.class){
if(lazyMan==null){
lazyMan = new LazyMan(); //不是一个原子性操作
}}
}
public static void main(String[] args){
for(int i = 0;i<10;i++){
new Thread(()->{
LazyMan.getInstance();
}).start();
}
}
}
若用下面方法破坏
public static void main(String[] args) throws Exception{
LazyMan instance = LazyMan.getInstance();
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null)
LazyMan instance = declaredConstructor.newInstance();
LazyMan instance2 = declaredConstructor.newInstance();
解决方法
public class LazyMan{
private static boolean qinjiang = false
private LazyMan(){
synchronized(LazyMan.class){
if(qinjiang == false){
qinjiang=true;
}else{
throw new RuntimeException("不要试图用反射破坏异常")
}
private volatile static LazyMan lazyMan; //加上volatile避免指令重排
//多线程并发修改,加锁,双重检测锁模式的懒汉式单例 DCL懒汉式
public static LazyMan getInstance(){
if(lazyMan==null){
synchronized(LazyMan.class){
if(lazyMan==null){
lazyMan = new LazyMan(); //不是一个原子性操作
}}
}
public static void main(String[] args){
for(int i = 0;i<10;i++){
new Thread(()->{
LazyMan.getInstance();
}).start();
}
}
}
再次破坏
public class LazyMan{
private static boolean qinjiang = false
private LazyMan(){
synchronized(LazyMan.class){
if(qinjiang == false){
qinjiang=true;
}else{
throw new RuntimeException("不要试图用反射破坏异常")
}
private volatile static LazyMan lazyMan; //加上volatile避免指令重排
//多线程并发修改,加锁,双重检测锁模式的懒汉式单例 DCL懒汉式
public static LazyMan getInstance(){
if(lazyMan==null){
synchronized(LazyMan.class){
if(lazyMan==null){
lazyMan = new LazyMan(); //不是一个原子性操作
}}
}
public static void main(String[] args) throws Exception{
// LazyMan instance = LazyMan.getInstance();
Field qinjiang = LazyMan.Class.getDeclaredField("qinjiang");
qinjiang.setAccessible(true);
LazyMan instance = LazyMan.getInstance();
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null)
declaredConstructor.setAccessible(true);
qinjiang.set(instance,false);
LazyMan instance = declaredConstructor.newInstance();
LazyMan instance2 = declaredConstructor.newInstance();
}
5.7
枚举
public enum EnumSingle{
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
Class Test{
public static void main(String[] args) throws NoSuchMethodExcepion,IllegalAccessible{
EnumSingle instance1 = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
EnumSingle instance2 = declaredConstructor.newInstance();
system.out.println(instance1);
system.out.println(instance2);
}
}
反射不能破坏枚举的单例。
6
工厂模式
6.1
作用
实现了创建者和调用者的分离
详细分类:
简单工厂模式
工厂方法模式
抽象工厂模式
OOP七大原则:
开闭原则:一个软件的实体应当对扩展开发,对修改关闭
依赖倒转原则:要针对接口编程,不要针对实现编程
迪米特法则:只与你直接的朋友通信,而避免与陌生人通信
6.2
核心本质
实例化对象不使用new,而用工厂方法代替
将选择实现类,创建对象同意管理和控制。从而将调用者跟我们的实现类解耦
三种模式:
简单工厂模式
用来生产同一等级结构中的任意产品(对于增加新的产品,需要覆盖已有代码)
工厂方法模式
用来生产同一等级结构中的固定产品(支持增加任意产品)
抽象工厂模式
围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。
新建factory包,包下新建simple包用来测试简单工厂模式
新建一个Car接口
package com.zhang.factory.simple;
public interface Car {
void name();
}
新建Wuling类
package com.zhang.factory.simple;
public class Wuling implements Car{
public void name() {
System.out.println("五菱之光");
}
}
新建Tesla类
package com.zhang.factory.simple;
public class Tesla implements Car {
public void name() {
System.out.println("特斯拉!");
}
}
新建消费者类Consumer
package com.zhang.factory.simple;
public class Consumer {
public static void main(String[] args) {
/*
* 之前的方式,需要接口,需要所有的实现类,相当于自己造了一辆车
* */
Car car = new Wuling();
Car car1 = new Tesla();
car.name();
car1.name();
}
}
新的方式,工厂模式,先造一个工厂,新建一个工厂类CarFactory
package com.zhang.factory.simple;
//简单工厂模式又叫静态工厂模式
// 增加一个新的产品,如果你不修改代码,做不到!
public class CarFactory {
public static Car getCar(String car){
if (car.equals("五菱")){
return new Wuling();
}else if (car.equals("特斯拉")){
return new Tesla();
}else {
return null;
}
}
}
采用工厂模式
package com.zhang.factory.simple;
public class Consumer {
public static void main(String[] args) {
/*
* 之前的方式,需要接口,需要所有的实现类,相当于自己造了一辆车
* */
// Car car = new Wuling();
// Car car1 = new Tesla();
//
// car.name();
// car1.name();
//工厂模式
Car car = CarFactory.getCar("五菱");
Car car1 = CarFactory.getCar("特斯拉");
car.name();
car1.name();
}
}
新建一个method包,将Car接口,Consumer类,Tesla类,wuling类复制到method包下
新建一个CarFactory接口
package com.zhang.factory.method;
//工厂方法模式
public interface CarFactory {
Car getCar();
}
新建TeslaFactory类
package com.zhang.factory.method;
public class TeslaFactory implements CarFactory{
public Car getCar() {
return new Tesla();
}
}
新建WulingFactory类
package com.zhang.factory.method;
public class WulingFactory implements CarFactory{
public Car getCar() {
return new Wuling();
}
}
重写Consumer类
package com.zhang.factory.method;
import com.zhang.factory.simple.CarFactory;
public class Consumer {
public static void main(String[] args) {
Car car = new WulingFactory().getCar();
Car car1 = new TeslaFactory().getCar();
car.name();
car1.name();;
}
}
简单工厂相对于工厂方法模式而言,其在结构复杂度、代码复杂度、编程复杂度、管理上的复杂度等方面具有优势,但根据设计原则:更需要使用工厂方法模式,但是根据实际业务而言,使用简单工厂模式。
7
抽象工厂模式
7.1
定义
抽象工厂模式提供了一个创建一系列相关或者相互依赖对象的接口,无序指定它们具体的类
7.2
适用场景
客户端(应用层)不依赖于产品类实例如何被创建、实现等细节
强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量的重复代码
提供一个产品类的库,所有的产品以同样的接口出现,从而使得客户端不依赖于具体的实现
7.3
优点
具体产品在应用层的代码隔离,无需关心创建的细节
将一个系列的产品统一到一起创建
7.4
缺点
规定了所有可能被创建的产品集合,产品簇中扩展新的产品困难
增加了系统的抽象性和理解难度
7.5
产品族和产品等级
如小米手机和华为手机和抽象的手机构成了同一产品等级的概念。
如小米手机和小米路由器构成了同一个产品族的概念。
所有同一个产品族中的东西都是由同一个工厂生产的,但是位于不同的等级结构。
新建一个Abstract的包
手机产品接口IPhoneProduct
package com.zhang.factory.Abstract;
//手机产品接口
public interface IphoneProduct {
void start();
void shudown();
void callup();
void sendSMS();
}
路由器产品接口IRouterProduct
package com.zhang.factory.Abstract;
//路由器产品接口IRouterProduct
public interface IRouterProduct {
void start();
void shutdown();
void openWifi();
void setting();
}
XiaomiPhone类
package com.zhang.factory.Abstract;
//小米手机
public class XiaomiPhone implements IphoneProduct{
public void start() {
System.out.println("小米手机开机");
}
public void shudown() {
System.out.println("小米手机关机");
}
public void callup() {
System.out.println("小米手机打电话");
}
public void sendSMS() {
System.out.println("小米手机发短信");
}
}
HuaweiPhone
package com.zhang.factory.Abstract;
public class HuaweiPhone implements IphoneProduct{
public void start() {
System.out.println("华为手机开机");
}
public void shudown() {
System.out.println("华为手机关机");
}
public void callup() {
System.out.println("华为手机打电话");
}
public void sendSMS() {
System.out.println("华为手机发短信");
}
}
XiaomiRouter类
package com.zhang.factory.Abstract;
public class XiaomiRouter implements IRouterProduct {
public void start() {
System.out.println("小米路由器启动");
}
public void shutdown() {
System.out.println("小米路由器关闭");
}
public void openWifi() {
System.out.println("小米路由器打开wifi");
}
public void setting() {
System.out.println("小米路由器设置");
}
}
HuaweiRouter类
package com.zhang.factory.Abstract;
public class HuaweiRouter implements IRouterProduct{
public void start() {
System.out.println("华为路由器启动");
}
public void shutdown() {
System.out.println("华为路由器关闭");
}
public void openWifi() {
System.out.println("华为路由器打开wifi");
}
public void setting() {
System.out.println("华为路由器设置");
}
}
抽象产品工厂IProductFactory接口
package com.zhang.factory.Abstract;
//抽象产品工厂
public interface IProductFactory {
//生产手机
IphoneProduct iphoneProduct();
//生产路由器
IRouterProduct routerProduct();
}
小米工厂XiaomiFactory
package com.zhang.factory.Abstract;
public class XiaomiFactory implements IProductFactory{
public IphoneProduct iphoneProduct() {
return new XiaomiPhone();
}
public IRouterProduct routerProduct() {
return new XiaomiRouter();
}
}
华为工厂HuaweiFactory
package com.zhang.factory.Abstract;
public class HuaweiFactory implements IProductFactory{
public IphoneProduct iphoneProduct() {
return new HuaweiPhone();
}
public IRouterProduct routerProduct() {
return new HuaweiRouter();
}
}
Client类
package com.zhang.factory.Abstract;
public class Client {
public static void main(String[] args) {
System.out.println("小米产品----------");
//小米工厂
XiaomiFactory xiaomiFactory = new XiaomiFactory();
IphoneProduct iphoneProduct = xiaomiFactory.iphoneProduct();
iphoneProduct.callup();;
iphoneProduct.sendSMS();
IRouterProduct iRouterProduct = xiaomiFactory.routerProduct();
iRouterProduct.openWifi();
System.out.println("华为产品----------");
//华为工厂
HuaweiFactory huaweiFactory = new HuaweiFactory();
IphoneProduct iphoneProduct1 = huaweiFactory.iphoneProduct();
iphoneProduct1.callup();;
iphoneProduct1.sendSMS();
IRouterProduct iRouterProduct1 = huaweiFactory.routerProduct();
iRouterProduct1.openWifi();
}
}
代码结构图
7.6
场景
- 简单工厂模式(静态工厂模式)
- 虽然某种程度上不符合设计原则,但实际使用最多!
- 工厂方法模式
- 不修改已有类的前提下,通过增加新的工厂类实现扩展
- 抽象工厂模式
- 不可以增加产品,可以增加产品族
7.7
应用场景
jdk中Calender的getInstance方法
jdbc中Connection对象的获取
Spring中IOC容器创建管理bean对象
反射中Class对象的newInstance方法
8
建造者模式
建造者模式也属于创建型模式,它提供了一种创建对象的最佳方式。
8.1
定义
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
8.2
主要作用
在用户不知道对象的建造过程和细节的情况下,就可以直接创建复杂的对象。
用户只需要给出指定复杂对象的类型和内容,建造者负责按顺序创建复杂模型。(把内部的建造过程和细节隐藏起来)
8.3
例子
工厂(建造者模式):负责制造汽车,组装过程和细节在工厂内。
汽车购买者(用户):你只需要说出你需要的型号(对象的类型和内容),然后直接购买就可以使用了,不需要知道汽车是怎么组装的(车门,车轮,发动机,方向盘等等)。
8.4
角色分析
既然是建造者模式,那我们继续造房吧,其实假设造房简化为如下步骤。:
一地基,二钢筋工程,三铺电线,四粉刷,如果要盖一座房子,首先要找一个建筑公司或工程承包商(指挥者),承包商指挥工人(具体建造者)过来造房子(产品),最后验收。
新建builder包
新建Builder类,抽象的建造者,不负责造房子,只是定义一些方法和接口
package com.zhang.builder;
public abstract class Builder {
abstract void buildA();//地基
abstract void buildB();//钢筋工程
abstract void buildC();//铺电线
abstract void buildD();//粉刷
//完工:得到产品
abstract Product getProduct();
}
Product类:房子
package com.zhang.builder;
public class Product {
private String buildA;
private String buildB;
private String buildC;
private String buildD;
public String getBuildA() {
return buildA;
}
public void setBuildA(String buildA) {
this.buildA = buildA;
}
public String getBuildB() {
return buildB;
}
public void setBuildB(String buildB) {
this.buildB = buildB;
}
public String getBuildC() {
return buildC;
}
public void setBuildC(String buildC) {
this.buildC = buildC;
}
public String getBuildD() {
return buildD;
}
public void setBuildD(String buildD) {
this.buildD = buildD;
}
public String toString() {
return "Product{" +
"buildA='" + buildA + '\'' +
", buildB='" + buildB + '\'' +
", buildC='" + buildC + '\'' +
", buildD='" + buildD + '\'' +
'}';
}
}
Worker类,具体的实现类
package com.zhang.builder;
//具体的建造者:工人
public class Worker extends Builder {
private Product product;
public Worker(){
product = new Product();
}
void buildA() {
product.setBuildA("地基");
System.out.println("地基");
}
void buildB() {
product.setBuildB("钢筋工程");
System.out.println("钢筋工程");
}
void buildC() {
product.setBuildC("铺电线");
System.out.println("铺电线");
}
void buildD() {
product.setBuildD("粉刷");
System.out.println("粉刷");
}
Product getProduct() {
return product;
}
}
Director类
package com.zhang.builder;
//指挥:核心,负责指挥构建一个工程,工程如何构建,由他决定
public class Director {
//指挥功能按照顺序建房子
public Product build(Builder builder){
builder.buildA();
builder.buildB();
builder.buildC();
builder.buildD();
return builder.getProduct();
}
}
Test类
package com.zhang.builder;
public class Test {
public static void main(String[] args) {
//指挥
Director director = new Director();
//指挥 具体的工人完成产品
Product build = director.build(new Worker());
System.out.println(build.toString());
}
}
上面示例是builder模式的常规用法。导演类director在builder模式中具有很重要的作用,它用于指导具体构建者如何构建产品,控制调用先后次序,并向调用者返回完整的产品类,但是有些情况下需要简化系统结构。可以把director和抽象构造建造者进行结合。
通过静态内部类方式实现零件无序装配构造,这种方式使用更加灵活,更符合定义。内部有复杂对象的默认实现,使用时可以根据用户需求自由定义更改内容,并且无需改变具体的构造方式。就可以生产出不同复杂产品。
比如,麦当劳的套餐,服务员(具体建造者)可以随意搭配任意几种产品(零件),组成一款套餐(产品),然后出售给客户。比第一种方式少了指挥者,主要是因为第二种方式把指挥者交给用户来操作,使得产品的构建更加简单灵活。
新建抽象类Builder
package com.zhang.builder.demo2;
//建造者
public abstract class Builder {
abstract Builder builderA(String msg); //汉堡
abstract Builder builderB(String msg); //可乐
abstract Builder builderC(String msg); //薯条
abstract Builder builderD(String msg); //甜点
abstract Product getProduct();
}
新建Product类
package com.zhang.builder.demo2;
//产品:套餐
public class Product {
private String BuildA = "汉堡";
private String BuildB = "可乐";
private String BuildC = "薯条";
private String BuildD = "甜点";
public String getBuildA() {
return BuildA;
}
public void setBuildA(String buildA) {
BuildA = buildA;
}
public String getBuildB() {
return BuildB;
}
public void setBuildB(String buildB) {
BuildB = buildB;
}
public String getBuildC() {
return BuildC;
}
public void setBuildC(String buildC) {
BuildC = buildC;
}
public String getBuildD() {
return BuildD;
}
public void setBuildD(String buildD) {
BuildD = buildD;
}
}
Worker类
package com.zhang.builder.demo2;
//具体的建造者
public class Worker extends Builder {
private Product product;
public Worker(){
product = new Product();
}
Builder builderA(String msg) {
product.setBuildA(msg);
return this;
}
Builder builderB(String msg) {
product.setBuildB(msg);
return this;
}
Builder builderC(String msg) {
product.setBuildC(msg);
return this;
}
Builder builderD(String msg) {
product.setBuildD(msg);
return this;
}
Product getProduct() {
return product;
}
}
Test类,没有指挥者,实际上的指挥者就是工人
package com.zhang.builder.demo2;
public class Test {
public static void main(String[] args) {
//服务员
Worker worker = new Worker();
Product product = worker.getProduct();
System.out.println(product.toString());
}
}
8.5
优点
产品的建造和表示分离,实现了解耦,使用建造者模式可以使客户端不必知道产品内部组成的细节。
将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰。
具体的建造者类之间是相互独立的,这有利于系统的扩展,增加新的具体建造者无需修改原有类库的代码,符合开闭原则。
8.6
缺点
建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
8.7
应用场景
需要生成的产品对象有复杂的内部结构,这些产品对象具备共性。
隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。
适合于一个具有较多的零件(属性)的产品(对象)的创建过程。
8.8
建造者与抽象工厂模式的比较
与抽象工厂模式相比,建造者模式返回一个组装好的完整产品,而抽象工厂模式返回一系列相关的产品,这些产品位于不同的产品等级结构,构成了一个产品族
在抽象工厂模式中客户端实例化工厂类,然后调用工厂方法获取所需产品对象,而在建造者模式中,客户端可以不直接调用建造者的相关方法,而是通过指挥者类来指导如何生成对象,包括对象的组装过程和建造步骤,他侧重于一步一步构建一个复杂对象,返回一个完整的对象
如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车。
END
以上是关于设计模式(01)的主要内容,如果未能解决你的问题,请参考以下文章
如何使用对象列表在片段中实现newinstace模式[重复]