设计模式的六大原则:
- 单一职责:一个类和方法只做一件事
- 开闭原则:对新增代码开放,对修改代码封闭
- 里氏替换:子类不要覆盖父类的方法(抽象方法除外)
- 接口隔离:不要在一个接口里面放很多的方法
- 依赖倒置:依赖关系是通过接口或抽象类产生的
- 迪米特法则:尽量降低类与类之间的耦合
单例模式
1️⃣核心思想
应用程序运行过程中,获得某一个类型的实例的时候,获得的始终是同一个实例。
分为三步:
- 构造方法私有
- 自身提供一个实例
- 提供一个静态方法供外部类获取该类的对象
2️⃣模式实现
-
懒加载:需要的时候就加载,当我们去调用到获得实例方法的才进行加载。
-
立即加载:马上加载,在我们获得实例之前已经完成加载。
①线程不安全的懒加载
public class MySingleton01 {
//自身提供一个实例
private static MySingleton01 mySingleton01;
//提供一个静态方法给外部类获得实例
public static MySingleton01 getInstance(){
//没有实例化则实例化
if (mySingleton01 == null){
mySingleton01 = new MySingleton01();
}
//已实例化则直接返回
return mySingleton01;
}
//构造方法私有
private MySingleton01(){}
}
②线程安全的懒加载
给获得实例的方法加个锁即可
public class MySingleton02 {
//自身提供一个实例
private static MySingleton02 mySingleton02;
//提供一个静态方法给外部类获得实例
public synchronized static MySingleton02 getInstance(){
//没有实例化则实例化
if (mySingleton02 == null){
mySingleton02 = new MySingleton02();
}
//已实例化则直接返回
return mySingleton02;
}
//构造方法私有
private MySingleton02(){}
}
③线程安全的立即加载
立即加载都是线程安全的,因为对象只会在类加载的时候创建
package singleton;
public class MySingleton03 {
//自身提供一个实例
private static MySingleton03 mySingleton03 = new MySingleton03();
//提供一个静态方法给外部类获得实例
public static MySingleton03 getInstance(){
return mySingleton03;
}
//构造方法私有
private MySingleton03(){}
}
④线程安全的立即加载(内部类)
package singleton;
public class MySingleton04 {
//提供一个静态方法给外部类获得实例
public static MySingleton04 getInstance(){
return Inner.mySingleton04;
}
private static class Inner{
//自身提供一个实例
public static MySingleton04 mySingleton04 = new MySingleton04();
}
//构造方法私有
private MySingleton04(){}
}
工厂模式
1️⃣核心思想
定义一个创建对象的接口。让子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类中进行
2️⃣模式实现
在接口中定义一个创建工厂类的方法
public interface CarFactory {
public Car creatCar();
}
在具体工厂中创建要对应的工厂类
public class TeslaFactory implements CarFactory{
public Car creatCar() {
return new TeslaCar();
}
}
代理模式
1️⃣核心思想
代理类实现了委托类要做的事,并且在做这些事的前后都可以做额外的事(增强)
2️⃣模式实现
①静态代理
委托类:
public class Client {
public void buyBreakfast(){
System.out.println("一碗热干面");
}
}
代理类继承委托类:
public class Agent extends Client {
@Override
public void buyBreakfast(){
super.buyBreakfast();
System.out.println("一杯蛋酒");
}
}
②动态代理
动态代理不需要新建代理类就可以获得代理类的对象
Ⅰ.JDK动态代理
使用条件:委托类必须实现一个接口,因为用该方法生成的代理对象也会继承该接口,并且只能代理执行该接口中定义的方法。
使用方法:
- 调用
Proxy.newProxyInstance(param1,param2,param3)
方法,该方法需要三个参数,第一个是类加载器,第二个是接口的Class类,第三个是实现了InvocationHandler
接口的对象,第三个参数可以使用内部类的方式。 - 这样就得到了一个与委托类实现了相同接口的代理对象,在该对象上调用方法就相当于调用
InvocationHandler
对象中的invoke()
方法。 - 因此我们可以在
invoke()
方法中执行委托类的方法,并且可以在invoke()
方法中做额外的事,这样,代理类在调用任何方法的时候,都会执行在invoke()
方法中额外做的事情。
使用案例:
假如现有接口BuyFood:
public interface BuyFood {
public String buyLunch(String food);
}
Client类实现了该接口:
public class Client implements BuyFood {
public String buyLunch(String food){
System.out.println(food);
return food;
}
}
获得代理类的对象并调用方法:
public class JDKDynamic {
public static void main(String[] args) {
BuyFood proxy = (BuyFood) Proxy.newProxyInstance(Client.class.getClassLoader(), Client.class.getInterfaces(), new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("一根油条");
Object invoke = method.invoke(Client.class.newInstance(), args);
System.out.println("一杯豆浆");
return invoke;
}
});
String s = proxy.buyLunch("一碗炒粉");
System.out.println("------------");
System.out.println(s);
}
}
输出结果为:
一根油条
一碗炒粉
一杯豆浆
------------
一碗炒粉
Ⅱ.cglib动态代理
使用条件:
导入依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.12</version>
</dependency>
使用方法:
- 调用
Enhance.create(param1, param2)
方法,该方法需要两个参数,第一个是委托类的Class类,第二个是实现了InvocationHandler
接口的对象。特别注意:这个接口与JDK动态代理的InvocationHandler
接口名字相同,但是包不同,所以不是同一个接口。 - 这样就得到了一个继承自委托类,并重写了委托类所有方法的对象。
- 后续与JDK动态代理一样。
使用案例:
有如下委托类没有实现任何接口:
public class Client {
public String buyLunch(String food){
System.out.println(food);
return food;
}
}
获得代理类并调用其方法:
public class CglibDynamic {
public static void main(final String[] args) {
Client proxy = (Client) Enhancer.create(Client.class, new InvocationHandler() {
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
System.out.println("一瓶可乐");
Object invoke = method.invoke(Client.class.newInstance(), objects);
System.out.println("一根烤肠");
return invoke;
}
});
String s = proxy.buyLunch("一个汉堡");
System.out.println("----------");
System.out.println(s);
}
}
输出结果为:
一瓶可乐
一个汉堡
一根烤肠
----------
一个汉堡
建造者模式
1️⃣核心思想
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
通俗来说,将方法的返回值设为该对象即可。
2️⃣模式实现
假如现在造一个人,有头、手、腿三部分,头分为IQ、EQ、头发,手和腿有长度
创建HumanBuilder:
public class HumanBuilder {
private Human human = new Human();
public HumanBuilder setHeadIQ(Integer IQ){
Head head = human.getHead();
head.setIQ(IQ);
return this;
}
public HumanBuilder setHeadEQ(Integer EQ){
Head head = human.getHead();
head.setEQ(EQ);
return this;
}
public HumanBuilder setHeadHair(String hair){
Head head = human.getHead();
head.setHair(hair);
return this;
}
public HumanBuilder setArmLen(Integer len){
Arm arm = human.getArm();
arm.setLen(len);
return this;
}
public HumanBuilder setLegLen(Integer len){
Leg leg = human.getLeg();
leg.setLen(len);
return this;
}
public Human build(){
return human;
}
}
测试:
@Test
public void test01(){
HumanBuilder hb = new HumanBuilder();
hb.setHeadIQ(100).setHeadEQ(99).setHeadHair("black").setArmLen(70).setLegLen(90);
Human build = hb.build();
System.out.println(build);
}
测试结果:
Human(head=Head(IQ=100, EQ=99, hair=black), arm=Arm(len=70), leg=Leg(len=90))