[TOC]
设计模式
学习资源
来源于b站狂神说java设计模式
七大原则
- 开闭原则:对拓展开放,对修改关闭。
- 里氏替换:继承必须确保超类所拥有的性质在子类中仍然成立。尽量不替换父类的方法
- 依赖倒置原则:面向接编程,而不是面向实现编程。
- 单一职责原则:控制类的大小、将类解耦、提高内聚性。
- 接口隔离原则:为各个类建立他们需要的专用接口。
- 迪米特法则:只与你的“朋友交谈”,不与陌生人交流。
- 合成复用原则:尽量先使用组合或者聚合等关联关系来使用,而不是继承和实现。
创建型模式 :cat:
单例模式
懒汉式
public class LazyMan {
private static LazyMan lazyMan;
private LazyMan(){}
public static LazyMan getInstance() throws InterruptedException {
if (lazyMan==null){
lazyMan=new LazyMan();
}
return lazyMan;
}
public static void main(String[] args) throws InterruptedException {
/**
* 线程不安全 a,b 同时进入==null
* 这样会导致都会new
* 单例创建不成功
*/
LazyMan a=LazyMan.getInstance();
LazyMan b=LazyMan.getInstance();
System.out.println(a.hashCode());
System.out.println(b.hashCode());
}
}
饿汉式
public class Hungry {
private static Hungry hungry = new Hungry();
private Hungry() {}
private static Hungry getInstance() {
return hungry;
}
public static void main(String[] args) {
/**
* 线程安全,但是较为浪费空间
* 如果没有用到则会一直占用空间
*/
Hungry a=Hungry.getInstance();
Hungry b=Hungry.getInstance();
System.out.println(a.hashCode());
System.out.println(b.hashCode());
}
}
双重检查锁
public class DoubleCheckLock {
/**
* 错误版,没有volatile,会导致可能存在的指令重排
* 对象初始化过程分为三步
* 1、分配空间
* 2、创建对象
* 3、对象指向空间
* correct step is 123,but maybe exist 132,when a process 13,b return a null object
*/
private static DoubleCheckLock doubleCheckLock;
private DoubleCheckLock() {
}
public static DoubleCheckLock getInstance() {
if (doubleCheckLock == null) {
synchronized (DoubleCheckLock.class) {
if (doubleCheckLock == null) {
doubleCheckLock = new DoubleCheckLock();
}
}
}
return doubleCheckLock;
}
public static void main(String[] args) {
DoubleCheckLock a=DoubleCheckLock.getInstance();
DoubleCheckLock b=DoubleCheckLock.getInstance();
System.out.println(a.hashCode());
System.out.println(b.hashCode());
}
}
//正确版
public class DoubleCheckLock {
/**
* 正确版,volatile防止指令重排
* 第一个volatile内的synchronized,进行线程同步防止指令,这样就能保证第二个if线程安全
*/
private static volatile DoubleCheckLock doubleCheckLock;
private DoubleCheckLock() {
}
public static DoubleCheckLock getInstance() {
if (doubleCheckLock == null) {
synchronized (DoubleCheckLock.class) {
if (doubleCheckLock == null) {
doubleCheckLock = new DoubleCheckLock();
}
}
}
return doubleCheckLock;
}
public static void main(String[] args) {
DoubleCheckLock a=DoubleCheckLock.getInstance();
DoubleCheckLock b=DoubleCheckLock.getInstance();
System.out.println(a.hashCode());
System.out.println(b.hashCode());
}
}
枚举
public enum EnumF {
/**
* 枚举可以保证单例,建议使用
* 反射安全
*/
INSTANCE;
private EnumF(){}
public static EnumF getInstance(){
return INSTANCE;
}
public static void main(String[] args) {
EnumF a=EnumF.getInstance();
EnumF b=EnumF.getInstance();
System.out.println(a.hashCode());
System.out.println(b.hashCode());
}
}
静态内部类
public class StaticClass {
/**
* 利用静态内部类可以天然访问外部类的方法
* 有反射,单例不安全
*/
private StaticClass(){}
public static StaticClass getInstance(){
return InnerClass.staticClass;
}
private static class InnerClass{
private static final StaticClass staticClass= new StaticClass();
}
}
工厂模式
厂,生产对象,创建者和调用者分离 不适用new,而使用工厂方法 选择实现类,统一完成对象管理和控制,从而将调用者跟我们的实现者解耦
简单工厂模式(静态工厂模式)
用于创建同一等级结构中的任意产品(对于增加新的产品,需要修改已有代码)
- 通过匹配字符串来在工厂里new对象
- 消费者与创建对象解耦
- 使用时需要修改Factory类的字符串匹配处 违反开闭原则 大多数用的这个,如果要遵守开闭原则,成本较大
- 方便调用,有些对象创建很麻烦
public interface Car {
void name();
}
class Tesla implements Car {
@Override
public void name() {
System.out.println("tesla complete");
}
}
class Wulin implements Car {
@Override
public void name() {
System.out.println("wulin complete");
}
}
class CarFactory {
public static Car getInstance(String carTypeName) {
switch (carTypeName) {
case "wulin":
return new Wulin();
case "tesla":
return new Tesla();
default:
return null;
}
}
}
class Consumer{
public static void main(String[] args) {
Car tesla=CarFactory.getInstance("tesla");
Car wulin=CarFactory.getInstance("wulin");
tesla.name();
wulin.name();
}
}
tesla complete
wulin complete
工厂方法模式
用工厂生产统一等级的固定产品(支持增加任意产品)
- 为了符合开闭原则,新增商品类,不需要修改工厂方法,更加符合开闭原则
- 将工厂方法也进行抽象,一个商品,对应一个具体的工厂,更符合现实的逻辑,例如丰田车有自己的工厂,本田车也有自己的工厂,而且新增新的商品,不需要修改工厂,只需要新增新的类工厂,符合[^开闭原则,拓展性更好]
- 类的数量会变得很多,一个商品对应一个工厂
package factory.method;
public interface Car {
void name();
}
class Tesla implements Car {
@Override
public void name() {
System.out.println("tesla complete");
}
}
class Wulin implements Car {
@Override
public void name() {
System.out.println("wulin complete");
}
}
interface CarFactory {
Car getCar();
}
class WulinCarFactory implements CarFactory {
@Override
public Car getCar() {
return new Wulin();
}
}
class TeslaCarFactory implements CarFactory {
@Override
public Car getCar() {
return new Tesla();
}
}
class Consumer {
public static void main(String[] args) {
Car wulin =new WulinCarFactory().getCar();
Car tesla= new TeslaCarFactory().getCar();
wulin.name();
tesla.name();
}
}
抽象工厂模式
围绕一个超级工厂创建其他工厂,超级工厂又称为其他工厂的工厂,[^套娃??]
-
看来并不是套娃,抽象工厂模式,是为了构建一个产品体系并创建体系中的对象
-
了解一个概念,产品家族和产品等级,例如中国石油生产93号汽油和天然气,中国石化生产的93号汽油和天然气。 中国石化生产的93号汽油和天然气就是一个产品家族,中国石化生产的93号汽油和中国石化生产的93号汽油是一个同等级的产品
-
将具体产品抽象成抽象产品,抽象产品可以构成抽象产品家族,这个家族中的每一员的具体类就是以后要生产的对象。例如中石化和中石油,所生产的产品,抽象出来的抽象产品家族就是93号汽油和天然气。
-
抽象工厂依赖于抽象产品家族,一个抽象工厂中有n(n等于抽象产品家族的成员数量)个方法,生产抽象的产品。具体的工厂类实现抽象抽象工厂,生产对应的家族的产品。
-
调用方法
消费者调用
抽象工厂 抽象工厂对象1=new 具体的产品工厂();
抽象产品1 具体对象=抽象工厂对象1.get抽象产品1(); ......
就获得了具体产品
-
缺点:如果产品家族经常变动,那么便不适合该模式,因为要频繁修改,太过繁琐,且违反开闭原则
-
优点:如果是已经成熟的产品家族,那么抽象工厂模式是一个非常强大的模式,可以随时拓展具体产品家族且创建任意具体产品对象。
package factory.abstractMethod;
//抽象产品家族(体系)>
public interface IPhoneProduct {
void sendMessage();
void start();
}
interface IComputerProduct {
void type();
void doSomeOffice();
}
//抽象产品家族(体系)<
//苹果具体产品家族 >
class ApplePhoneProduct implements IPhoneProduct {
@Override
public void sendMessage() {
System.out.println("iPhone发短消息了");
}
@Override
public void start() {
System.out.println("iPhone开机了");
}
}
class MakBookProduct implements IComputerProduct {
@Override
public void type() {
System.out.println("macBook打字");
}
@Override
public void doSomeOffice() {
System.out.println("macBook办公");
}
}
//苹果具体产品家族 <
//小米具体产品家族 >
/**
* 楼主🐖 刚订购了K30S,有点小激动
*/
class K30S implements IPhoneProduct {
@Override
public void sendMessage() {
System.out.println("小米手机发短信了");
}
@Override
public void start() {
System.out.println("小米手机开机了");
}
}
class XiaomiBook implements IComputerProduct {
@Override
public void type() {
System.out.println("小米笔记本打字了");
}
@Override
public void doSomeOffice() {
System.out.println("小米笔记本办公");
}
}
//小米具体产品家族 <
//抽象工厂 >
/**
* 抽象工厂依赖于抽象产品家族
*/
interface AbstractFactory {
IPhoneProduct getPhone();
IComputerProduct getComputer();
}
//抽象工厂 <
//苹果具体的工厂 >
/**
* 继承了 抽象工厂的具体工厂 依赖于继承了 抽象产品家族的具体产品家族
* 具体的产品工厂负责生产具体产品
*/
class AppleFactory implements AbstractFactory{
@Override
public IPhoneProduct getPhone() {
return new ApplePhoneProduct();
}
@Override
public IComputerProduct getComputer() {
return new MakBookProduct();
}
}
//苹果具体的工厂 <
//小米的具体工厂 >
class XiaomiFactory implements AbstractFactory{
@Override
public IPhoneProduct getPhone() {
return new K30S();
}
@Override
public IComputerProduct getComputer() {
return new XiaomiBook();
}
}
//小米的具体工厂 <
//消费者 >
class Client{
public static void main(String[] args) {
//先创建具体的工厂 抽象工厂-》具体家族工厂 多态
AbstractFactory appleFactory = new AppleFactory();
AbstractFactory xiaomiFactory = new XiaomiFactory();
//再获得具体的产品 抽象产品->具体产品 多态
IPhoneProduct iPhone=appleFactory.getPhone();
IComputerProduct mac=appleFactory.getComputer();
IPhoneProduct k30S=xiaomiFactory.getPhone();
IComputerProduct xiaomiBook=xiaomiFactory.getComputer();
iPhone.start();
iPhone.sendMessage();
mac.doSomeOffice();
mac.type();
k30S.start();
k30S.sendMessage();
xiaomiBook.type();
xiaomiBook.doSomeOffice();
}
}
//消费者 <
| 工厂模式类别 | 总结(差异) |
|---|---|
| 简单工厂模式(静态工厂模式) | 1.适用于同一级别的产品,修改会违反开闭原则,但是因为简单好用,这种模式是使用最多的 |
| 抽象方法工 | 1. 适用于同一级别的产品,修改不会影响开闭原则,但是类会变多 |
| 抽象工厂模式 | 1. 使用于有产品家族的(产品),不能频繁修改产品家族的成员,但是可以方便的引入新的产品体系。2.前面两个是生产同一级别的产品,是线。抽象工厂是生产成体系的家族产品,是面。3.我更愿意叫抽象工厂模式为抽象家族(体系)产品工厂模式;抽象产品为抽象家族(体系)产品;具体产品为具体家族(体系)产品;抽象工厂为依赖于抽象家族产品的抽象工厂,且厂内有n(n=抽象家族产品的成员数量)个方法;具体工厂为依赖于具体产品的且继承抽象工厂的具体工厂。 |
建造者模式
建造者模式也是创建者模式的一种,提供了创建对象的最佳方式[^回顾一下建造者模式是帮助创建对象时省new的模式] ,复杂对象,不同表示
生产者生产零件,建造者负责构建???
原型模式