带你以第一人称的视角了解23种设计模式-java版
设计模式的分类
总体来说设计模式分为三大类:
- 创建型模式,共五种:工厂模式、抽象工厂模式、单例模式、建造者模式、原型模式。
- 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
- 行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
1.建造者模式
你现在是一个土豪,你想要买这样的一个房子:
public class House {
private String buildA;//地基
private String buildB;//墙体
private String buildC;//粉刷
private String buildD;//装修
//get和set方法
}
所以你准备招聘一个指挥者来造你的房子:
public class Direct {
public House build(Builder builder) {
builder.buildA();//地基
builder.buildB();//墙体
builder.buildC();//粉刷
builder.buildD();//装修
return builder.getHouse();//得到房子
}
}
但是光有指挥者还不够啊,他需要自己招聘有以下能力的工人来完成这项任务,现在他发布了一个招聘信息如下:
public abstract class Builder {
abstract void buildA();//地基
abstract void buildB();//墙体
abstract void buildC();//粉刷
abstract void buildD();//装修
abstract House getHouse();//得到房子
}
经过他在社会上的威望,不久就找到了工人:
public class Worker extends Builder{
private House house;
public Worker() {
house = new House();
}
@Override
void buildA() {
house.setBuildA("地基");
System.out.println("地基");
}
@Override
void buildB() {
house.setBuildB("墙体");
System.out.println("墙体");
}
@Override
void buildC() {
house.setBuildC("粉刷");
System.out.println("粉刷");
}
@Override
void buildD() {
house.setBuildD("装修");
System.out.println("装修");
}
@Override
House getHouse() {
return house;
}
}
指挥者打了一个电话给你说:老兄,兄弟们已经到齐了,你拿钱就可以开工了!!!
然后你就一声令下,兄弟们,干就完了!!!:
public class Consumer {
public static void main(String[] args) {
//创建指挥者
Direct direct = new Direct();
//指挥者建造工人
House build = direct.build(new Worker());
System.out.println(build.toString());
}
}
奈何老板大气,一下就把房子建好了:
然后你给这个模式命名,就叫建造者模式
建造者模式的优缺点:
2.工厂模式
工厂模式分为简单工厂模式
及工厂方法模式
例如:
你想要买一辆车,肯定不能自己new一辆车出来,这个时候就需要工厂来帮忙:
public class CarFactory {
//方法一
// public static Car getCar(String name) {
// if ("aodi".equals(name)) {
// return new AoDi();
// }else if ("baoma".equals(name)) {
// return new Baoma();
// }else {
// return null;
// }
// };
//方法二
public static Car getAoDi() {
return new AoDi();
}
public static Car getBaoMa() {
return new Baoma();
}
}
但是我们还需要车,所以我们创建两辆车的品牌:
public interface Car {
void getname();
}
public class Baoma implements Car {
@Override
public void getname() {
System.out.println("宝马");
}
}
public class AoDi implements Car {
@Override
public void getname() {
System.out.println("奥迪");
}
}
这个时候如果你来买车,直接呼叫工厂给你创建一辆汽车就可以了:
public class Consumer {
public static void main(String[] args) {
//方法一
// Car car = CarFactory.getCar("aodi");
// car.getname();
//方法二
Car aoDi = CarFactory.getAoDi();
aoDi.getname();
Car baoMa = CarFactory.getBaoMa();
baoMa.getname();
}
}
这就是简单工厂模式
开闭原则: 对拓展开放,对修改关闭
里氏替换原则: 继承必须保证超类所拥有的性质在子类中仍然成立
依赖倒置原则: 要面向接口编程,不要面向现实
单一职责原则: 控制类的粒度大小,将对象解耦,提高内聚
接口隔离原则: 要用各个类建立他们需要的专用接口
迪米特法则: 只与直接朋友交谈,不与“陌生人”通信
合成复用原则: 尽量先使用组合或者内聚等关联关系来实现,其次才考虑使用继承来实现
但是违背了oop原则中的开闭原则
所以,这个时候就诞生了方法工厂模式:
我们创建一个总的工厂:
public interface CarFactory {
Car getCar();
}
然后每个车的品牌创建一个工厂实现总工厂类:
public class AoDifactory implements CarFactory {
@Override
public Car getCar() {
return new AoDi();
}
}
public class BaoMafactory implements CarFactory {
@Override
public Car getCar() {
return new Baoma();
}
}
所以这个时候就改进了oop原则中的开闭原则,这就是工厂方法模式
但是我们会发现这个模式会出现很多代码,看起来项目很臃肿:
所以我们在写代码的时候要按照项目来决定到底用哪一种方法。
3.抽象工厂模式
简而言之,抽象工厂模式就是定义工厂的工厂,它不像工厂模式那样是一个产品线,更像是一个产品簇
我们来用以下的例子来说明该模式:
我们首先要定义一个接口说明手机能做什么:
public interface PhoneProduct{
//开机
void start();
//关机
void shutdown();
//发短信
void sms();
}
我们需要造华为和小米的手机:
public class XiaoMiPhone implements PhoneProduct {
@Override
public void start() {
System.out.println("开启小米手机");
}
@Override
public void shutdown() {
System.out.println("关闭小米手机");
}
@Override
public void sms() {
System.out.println("小米手机发短信");
}
}
public class HuaWeiPhone implements PhoneProduct {
@Override
public void start() {
System.out.println("开启华为手机");
}
@Override
public void shutdown() {
System.out.println("关闭华为手机");
}
@Override
public void sms() {
System.out.println("华为手机发短信");
}
}
这个时候按照工厂模式来说应该有一个建造他们的工厂:
public class HuaWeifactory{
public HuaWeiPhone phoneProduct() {
return new HuaWeiPhone();
}
}
public class XiaoMifactory{
public XiaoMiPhone phoneProduct() {
return new XiaoMiPhone();
}
}
我们开始说过抽象工厂模式是一个产品簇,所以华为和小米工厂不止只想做手机,它们还想做路由器所以我们又给他添加一个新的产品:
public class HuaWeifactory {
//生产手机
public HuaWeiPhone phoneProduct() {
return new HuaWeiPhone();
}
//生产路由器
public HuaWeiRouter routerProduct() {
return new HuaWeiRouter();
}
}
public class XiaoMifactory {
@Override
public XiaoMiPhone phoneProduct() {
return new XiaoMiPhone();
}
@Override
public XiaoMiRouter routerProduct() {
return new XiaoMiRouter();
}
}
和手机一样,我们也需要路由器的实体类和一个规定路由器能做什么的接口:
public interface RouterProduct{
//开机
void start();
//关机
void shutdown();
//打开wifi
void openWifi();
}
public class HuaWeiRouter implements RouterProduct {
@Override
public void start() {
System.out.println("启动华为路由器");
}
@Override
public void shutdown() {
System.out.println("关闭华为路由器");
}
@Override
public void openWifi() {
System.out.println("启动华为wifi");
}
}
public class XiaoMiRouter implements RouterProduct {
@Override
public void start() {
System.out.println("启动小米路由器");
}
@Override
public void shutdown() {
System.out.println("关闭小米路由器");
}
@Override
public void openWifi() {
System.out.println("启动小米wifi");
}
}
但是我们开头又说了:抽象工厂模式就是定义工厂的工厂
所以我们还是需要定义个总工厂来管理华为和小米工厂,我们需要让该工厂决定华为和小米工厂可以生产什么东西:
public interface ProductFactory {
//生产手机
PhoneProduct phoneProduct();
//生产路由器
RouterProduct routerProduct();
}
所以华为和小米的工厂经过我们的改造现在变成了这样:
华为:
public class HuaWeifactory implements ProductFactory {
@Override
public PhoneProduct phoneProduct() {
return new HuaWeiPhone();
}
@Override
public RouterProduct routerProduct() {
return new HuaWeiRouter();
}
}
小米:
public class XiaoMifactory implements ProductFactory {
@Override
public PhoneProduct phoneProduct() {
return new XiaoMiPhone();
}
@Override
public RouterProduct routerProduct() {
return new XiaoMiRouter();
}
}
这个时候你就来购物了:
public class Consumer {
public static void main(String[] args) {
//创建华为工厂
HuaWeifactory huaWeifactory = new HuaWeifactory();
//华为工厂生产手机
PhoneProduct phoneProduct = huaWeifactory.phoneProduct();
phoneProduct.shutdown();
phoneProduct.start();
phoneProduct.sms();
//创建小米工厂
XiaoMifactory xiaoMifactory = new XiaoMifactory();
//小米工厂生产路由器
RouterProduct routerProduct = xiaoMifactory.routerProduct();
routerProduct.start();
routerProduct.shutdown();
routerProduct.openWifi();
}
}
购买完成之后你开始使用了:
这就是抽象工厂模式
但是我们在使用的过程中发现了该模式是一个产品簇,一旦我们需要添加新的产品的时候非常的困难,所以如果你的项目确定了具体的结构没有很多的改变还是可以用这种模式的,因为很多的类具体做什么事情不需要你具体的去操心。
抽象工厂模式的优缺点:
4.原型模式
你现在有一个女朋友:
public class Prototype implements Cloneable{
private String name;
private String hobby;
private Date birthday;
@Override
public String toString() {
return "Prototype{" +
"name='" + name + '\'' +
", hobby='" + hobby + '\'' +
", birthday=" + birthday +
'}';
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Prototype(String name, String hobby, Date birthday) {
this.name = name;
this.hobby = hobby;
this.birthday = birthday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
但是你并不满足,你还想再要一个所以你准备再克隆一个出来:
public class CopyPrototype implements Cloneable{
public static void main(String[] args) throws CloneNotSupportedException {
Date date = new Date();
Prototype prototype = new Prototype("小芊","听歌",date);
Prototype clone = (Prototype) prototype.clone();
System.out.println(prototype);
System.out.println(clone);
System.out.println("===============================");
date.setTime(1234566545);
System.out.println(prototype);
System.out.println(clone);
}
}
你本来想改变她的生日,但是你发现这个该了以后,新女朋友的生日居然也变了:
这可不行啊(这就叫浅克隆
- 浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于
非基本类型属性
,仍指向原有属性所指向的对象的内存地址。 - 深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。)
所以你想深克隆:
这个时候你需要把原有克隆里的方法的属性也克隆一下:
@Override
protected Object clone() throws CloneNotSupportedException {
Object clone = super.clone();
Prototype p = (Prototype) clone;
p.birthday = (Date) this.birthday.clone();
return clone;
}
这个时候再运行:
成功克隆出一个和原来不一样的女朋友
这就叫原型模式的深克隆
结构型模式
1.适配器模式:
你现在买了一台新电脑,家里有根网线:
public class Adaptee {
public void request() {
System.out.println("连接网线上网");
}
}
但是你发现电脑是超薄本,不能插网线,所以你需要一个适配器:
public interface NetUsb {
//作用: 处理请求
public void handleRequest();
}
public class Adapter extends Adaptee implements NetUsb {
@Override
public void handleRequest() {
super.request();
}
}
这个时候你的电脑就可以上网了:
public class Computer {
public void net(NetUsb adapter) {
//上网的转接头
adapter.handleRequest();
}
public static void main(String[] args) {
Computer computer = new Computer();
Adaptee adaptee = new Adaptee();
Adapter adapter = new Adapter();
computer.net(adapter);
}
}
这就是适配器模式
2.桥接模式
桥接模式就是把两个不相干的东西结合起来让他们有关联
你想买一台电脑:
public abstract class Computer {
private Brand brand;
public Computer(Brand brand) {
this.brand = brand;
}
public void info() {
brand.info();
}
}
但是不知道有哪些牌子:
public interface Brand {
void info();
}
现有:台式机和笔记本:
public class Desktop extends Computer {
public Desktop(Brand brand) {
super(brand);
}
@Override
public void info() {
super.info();
System.out.println("台式机");
}
}
public class Book extends Computer {
public Book(Brand brand) {
super(brand);
}
@Override
public void info() {
super.info();
System.out.println("笔记本");
}
}
还有两个品牌:华为和小米:
public class HuaWei implements Brand {
@Override
public void info() {
System.out.print("华为");
}
}
public class XiaoMi implements Brand {
@Override
public void info() {
System.out.print("小米");
}
}
现在你想好了,你需要一台华为台式机和小米笔记本:
public class Test {
public static void main(String[] args) {
//华为台式机
Computer computer = new Desktop(new HuaWei());
computer.info();
//小米笔记本
Computer computer1 = new Book(new XiaoMi());
computer1.info();
}
}