设计模式
1.七大原则
1.单一职责原则
(每个类或者每个方法只需要实现一个职责,不需要实现很多其他的功能。)在类中的方法足够多需要在类的层面遵守单一职责原则
方法遵循单一职责代码实现:
package com.example;
public class SingleResponsibility {
public static void main(String[] args) {
Vehicle vehicle = new Vehicle();
vehicle.runAir("飞机");
vehicle.runRolad("车子");
vehicle.runWater("游艇");
}
}
class Vehicle{
public void runRolad(String vehicle){
System.out.println(vehicle + "在公路上面运行");
}
public void runWater(String vehicle){
System.out.println(vehicle + "在水中上面运行");
}
public void runAir(String vehicle){
System.out.println(vehicle + "在天上运行");
}
}
2.接口隔离原则
A通过接口B依赖于C(只要实现接口B的1,2,3方法),D通过接口B依赖于F(只需要 实现接口B的1,4,5方法),这样A就得实现所有的B接口的方法,D就得实现所有的B接口方法,导致浪费,且相关性强,所以得遵循接口最小原则,把 方法1 放在一个接口Interface1, 方法 2,3放在一个接口Interface2 ,方法4,5放在一个接口Interface3.
3.依赖倒转原则
- 高层模块不应该依赖低层模块,二者应该依赖其抽象(接口和抽象类)
- 抽象不应该依赖细节,细节(一般是java的实现类)应该依赖抽象
- 、依赖倒转的中心思想是面向接口编程
- 设计理念:以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。
- java里面接口或者抽象类的目的是设计好规范(不去实现),实现一定是由那个实现类去操作。
- 继承时遵循里氏替换原则
代码实现
有问题代码
package com.monkey.single.inversion;
public class DependenceInversion {
public static void main(String[] args) {
Person person = new Person();
person.receiveMessage(new Email());
}
}
class Email{
public String getInfo(){
return "email,helloWorld";
}
}
//1.这个只可以接收邮箱的消息,不可以接收其他的比如微信,支付宝,短信的消息。
//2.我们可以来创建一个接口IReceiver(),这样Person类和接口IReceiver发生依赖
//因为Email,WeChat等等属于接收的范围,他们各自实现IReceiver接口就行,这样就发生了依赖倒转。
class Person{
// 这里是对实现类进行了依赖
public void receiveMessage(Email email){
System.out.println(email.getInfo());
}
}
实现依赖倒转原则以后的代码
package com.monkey.single.inversion;
public class DependenceInversion {
public static void main(String[] args) {
Person person = new Person();
person.receiveMessage(new Email());
person.receiveMessage(new Wexin());
}
}
interface Ireceiver{
public String getInfo();
}
class Email implements Ireceiver{
public String getInfo(){
return "email,helloWorld";
}
}
class Wexin implements Ireceiver{
public String getInfo(){
return "wexin,helloWorld";
}
}
//1.这个只可以接收邮箱的消息,不可以接收其他的比如微信,支付宝,短信的消息。
//2.我们可以来创建一个接口IReceiver(),这样Person类和接口IReceiver发生依赖
//因为Email,WeChat等等属于接收的范围,他们各自实现IReceiver接口就行,这样就发生了依赖倒转。
class Person{
// 这里是对接口进行依赖,稳定性好
public void receiveMessage(Ireceiver ireceiver){
System.out.println(ireceiver.getInfo());
}
}
实现接口间依赖传递的三种方式:
- 通过接口传递实现依赖
- 通过构造方法依赖传递
- 通过setter方法依赖传递
代码:
4.里氏替换原则
5.开闭原则
6.迪米特法原则
7.合成复用原则
经典面试题目?
-
在项目的实际开发中哪里使用了ocp原则? (工厂模式中有用)
-
spring中应用了哪些设计模式,原型模式在哪里 用到?
-
什么是解释器设计模式?画出它的UML类图,分析各个角色是什么?
-
spring框架中哪里使用到了解释器设计模式?并做源码级别分析
-
单例模式一共有几种实现方式?用代码实现,并说明各个的优点缺点。
单列设计模式有几种实现方式?
-
饿汉式 两种
-
懒汉式 三种
-
双重检查
-
静态内部类
设计模式概念:(design pattern)是对在软件设计普遍存在(反复出现)的各种问题,提出 的一种解决方案。
使用设计模式可以使用项目具有:可扩展性,维护性,可靠性(新增一个功能对原来的功能影响不大),可复用性,效率提高等等
设计模式:
1.单例模式:
饿汉模式:
package com.monkey.sigleton;
/*
* 饿汉模式:
* 类加载就实例化,JVM保证线程安全
* 推荐使用
* 唯一缺点: 不管使用与与否,类加载就会完成实例化
*
* */
public class HungryMode {
private static final HungryMode Instance = new HungryMode();
private HungryMode(){};
public static HungryMode getInstance(){
return Instance;
}
public static void main(String[] args) {
HungryMode instance = HungryMode.getInstance();
HungryMode instance1 = HungryMode.getInstance();
System.out.println(instance == instance1);
}
}
懒汉模式(双重if判断):
package com.monkey.sigleton;
public class LazyModeImprove {
private static LazyModeImprove Instance;
private LazyModeImprove(){};
private static LazyModeImprove getInstance(){
if(Instance == null){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (LazyModeImprove.class){
if(Instance == null){
Instance = new LazyModeImprove();
}
}
}
return Instance;
}
public static void main(String[] args) {
for (int i = 0; i < 1000; i++) {
new Thread(()->
System.out.println(Instance.getInstance())
).start();
}
}
}
懒汉模式静态内部类:
package com.monkey.sigleton;
public class LazyModeStaicInnerClass {
private LazyModeStaicInnerClass(){};
private static class SingletonInstance{
private static final LazyModeStaicInnerClass Instance = new LazyModeStaicInnerClass();
}
public static LazyModeStaicInnerClass getInstance(){
return SingletonInstance.Instance;
}
}
枚举单例模式:
package com.monkey.sigleton;
public enum EnumMode{
// 1. 可以防止反序列化,也是线程安全的
INSTANCE;
public void hellowWord(){
System.out.println("hello Word");
}
public static void main(String[] args) {
INSTANCE.hellowWord();
}
}
2.策略模式(把相同行为的不同实现进行封装)
package com.monkey.strategy.improve;
public abstract class Duck {
FlyBehavior flyBehavior;
public Duck() {
}
public abstract void display();//显示鸭子信息
public void setFlyBehavior(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
}
public void quack(){
System.out.println("鸭子会嘎嘎叫");
}
public void swim(){
System.out.println("鸭子会游戏");
}
// public void fly(){
// System.out.println("鸭子会飞行");
// }
// improve
public void fly(){
if(flyBehavior !=null){
flyBehavior.fly();
}
}
}
package com.monkey.strategy.improve;
public interface FlyBehavior {
void fly(); //子类具体实现
}
package com.monkey.strategy.improve;
public class GoodFlyBehavior implements FlyBehavior{
@Override
public void fly() {
System.out.println("飞翔技术高超");
}
}
package com.monkey.strategy.improve;
public class GoodFlyBehavior implements FlyBehavior{
@Override
public void fly() {
System.out.println("飞翔技术高超");
}
}
package com.monkey.strategy.improve;
public class PekingDuck extends Duck {
PekingDuck(){
flyBehavior = new BadFlyBehavior();
}
@Override
public void display() {
System.out.println("我是北京鸭");
}
//北京鸭不能飞行,这里需要重写方法
/* @Override
public void fly() {
System.out.println("北京鸭不能飞行");
}*/
}
package com.monkey.strategy.improve;
public class ToyDuck extends Duck {
ToyDuck(){
flyBehavior = new BadFlyBehavior();
}
@Override
public void display() {
System.out.println("我是玩具鸭");
}
// 需要重写父类的所有方法
public void quack(){
System.out.println("玩具鸭子不会嘎嘎叫");
}
public void swim(){
System.out.println("玩具鸭子不会游戏");
}
// public void fly(){
// System.out.println("玩具鸭子不会飞行");
// }
}
package com.monkey.strategy.improve;
public class WildDuck extends Duck {
WildDuck(){
flyBehavior = new GoodFlyBehavior();
}
@Override
public void display() {
System.out.println("我是野鸭子");
}
}
3. 享元模式(Flyweight pattern)
场景应用:**数据库连接池,缓冲池,Integer的底层源码,String常量池等等。**可以解决重复对象内存浪费问题。
理解:需要的我们拿来使用,不需要的直接创建 new一个。
String str = "hello";
String s2 = new String("hello ");
**内部状态:**对象共享出来的信息,且不随享元对象的外部状态改变而改变。
**外部状态:**随环境的改变而改变,不可共享的一个状态。
public class IntegerCode {
public static void main(String[] args) {
Integer integer = Integer.valueOf(126);
Integer integer1 = Integer.valueOf(126);
System.out.println(integer == integer1);
Integer integer2 = Integer.valueOf(128);
Integer integer3 = Integer.valueOf(128);
System.out.println(integer2 == integer3);
}
}
//里面的Integer.valueOf()使用了享元模式,如果大小在[-128,127]之间使用同一个数组里面的数据,否则重新创建一个Integer类型对象,减少了内存的消耗。
4.代理模式
为一个对象提供一个替身,以控制这个对象的访问。即 通过代理对象访问目标对象。
被代理的对象可以是 远程对象,创建开销大的对象,或需要安全控制的对象。
主要有:动态代理(也叫jdk代理),静态代理,cglib代理(可以在内存动态的创建对象,不需要实现接口,属于动态代理)。
在有些情况下,我们不可以直接访问目标对象,需要通过另外一个对象进行代理访问。
静态代理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-owrRldFi-1659411175436)(C:\Users\24473\AppData\Roaming\Typora\typora-user-images\image-20220706230151861.png)]
动态代理(JDK代理)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ludHQFDo-1659411175438)(C:\Users\24473\AppData\Roaming\Typora\typora-user-images\image-20220709173609450.png)]
图片上面是复用反射机制
可以看到代理对象不用实现目标接口
其实是通过 java的反射机制实现,然后通过代理对象进行调用ITeacherDao方法。
代理对象=增强内容+原对象。
其实就是spring中的aop
我们想在当前的业务逻辑下添加一些其他的处理,比如日志、校验等等,就不得不侵入原有的业务代码,尤其是在重重继承关系复杂的类中,需要增加一些内容,并不清楚会不会影响到其他功能,所以使用代理来实现需要增加的内容
package com.monkey.proxy.dynamic;
public interface IteacherDao {
void teach();//
String sayProxyStart(String proxyName);
}
package com.monkey.proxy.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyFactory {
private Object target; //代理的目标对象
// 有参树构造器,对target 进行初始化
public ProxyFactory(Object target) {
this.target = target;
}
//给目标对象通过 JDK的API 生成代理对象的方法
public Object getProxyInstace(){
// ClassLoader loader 类加载器
// 2. Class<?> interface
// 3. InvocationHandler
//
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("jdk动态代理开始");
Object object = method.invoke(target, args);
return object;
}
});
}
}
package com.monkey.proxy.dynamic;
import com.monkey.proxy.ITeacherDao;
public class Teacher implements IteacherDao {
@Override
public void teach() {
System.out.println("老师正在授课中。。。。。。。。。。");
}
@Override
public String sayProxyStart(String proxyName) {
return proxyName;
}
}
package com.monkey.proxy.dynamic;
public class Client {
public static void main(String[] args) {
Teacher target = new Teacher();
// 需要强制类型转换为ITeacherDao才可以调用到 接口里面的方法
IteacherDao proxyInstace = (IteacherDao)new ProxyFactory(target).getProxyInstace();
System.out.println("proxyInstace"+proxyInstace);
// 调用代理对象的方法
proxyInstace.teach();
String hello_proxy = proxyInstace.sayProxyStart("hello Proxy");
System.out.println(hello_proxy);
}
}
cglib代理
静态代理类和jdk代理都需要我们的目标对象实现一个接口,但是有些时候我们希望这个目标对象没有实现接口,可以使用目标对象的子类实现代理,这就是cglib代理。
cglib是一个强大的,高性能的代码生成包,它可以在运行期扩展java类实现java接口,(SpringAop中有使用,实现方法拦截。)
在Aop编程中如何选用代理模式:
- 目标对象需要实现接口,使用jdk代理(动态代理)
- 目标对象不需要实现接口,用cglib代理。
cglib底层是使用字节码处理框架ASM,转换字节码,并生成新的类。
ProxyFactory(代理对象需要 implements MethodInterceptor) MethodInterceptor是框架待的一个接口方法拦截,里面重写了 interceptor方法
5.模板方法模式
在一个抽象类中公开定义了一个执行它的方法模板,它的子类可以进行重写(关键的步骤)方法来实现,但调用将以抽象类中定义的方式进行。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ISCrvslB-1659411189861)(C:\Users\24473\AppData\Roaming\Typora\typora-user-images\image-20220714230054147.png)]
重点
- 模板方法是用final修饰的不可以进行重写
- 里面调用了常用的其他制作方法,可以进行重写关键的方法。
- 关键的方法是不用写相关的实现的。
- 基本流程实现的相同,需要写上相关的实现。
代码实现:
package com.monkey.template;
public abstract class SoyaMilk {
// 制作豆浆模板
final void make(){
select();
if(customerAddCondiments()){
addCondiments();
}
soak();
beat();
}
//选材料
void select(){
System.out.println("第一步,选择好新鲜的黄豆");
}
//添加不同的配料,抽象方法,子类具体实现
abstract void addCondiments();
boolean customerAddCondiments(){
return true;
}
//浸泡
void soak(){
System.out.println("第三步,黄豆进行浸泡");
}
void beat(){
System.out.println("第四步,黄豆打碎");
}
}
package com.monkey.template;
public class RedBeanSoyaMilck extends SoyaMilk{
@Override
void addCondiments() {
System.out.println("加入上好的红豆豆浆");
}
}
package com.monkey.template;
public class PureSoyaMilk extends SoyaMilk{
@Override
void addCondiments() {
//纯的豆浆不用添加
}
@Override
boolean customerAddCondiments() {
//因为是纯的豆浆不用添加任何其他的东西
return false;
}
}
package com.monkey.template;
public class PeanutSoyaMilk extends SoyaMilk{
@Override
void addCondiments() {
System.out.println("加入上好的花生豆浆");
}
}
package com.monkey.template;
public class Client {
public static void main(String[] args) {
System.out.println("红豆豆浆开始制作");
SoyaMilk soyaMilk = new RedBeanSoyaMilck();
soyaMilk.make();
System.out.println("花生豆浆开始制作");
SoyaMilk peanutSoyaMilk = new PeanutSoyaMilk();
peanutSoyaMilk.make();
System.out.println("纯豆浆开始制作");
SoyaMilk pureSoyaMilk = new PureSoyaMilk();
pureSoyaMilk.make();
}
}
6.观察者模式
又叫发布-订阅模式,模型视图模式, 从属模式, 源-监听模式。
概念:**定义了一对多的依赖关系,让多个观察者同时监听某个主题对象。**主题对象在状态上发生变化,会同时通知所有观察者对象,使他们同时更新自己。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sIalrkBc-1659411203154)(C:\Users\24473\AppData\Roaming\Typora\typora-user-images\image-20220722222223344.png)]
改进观察者好处:
- 观察者模式设计后会心集合方式管理用户(Observer),包括register,remove,notify.
- 这样观察者就是一个新的公告板,不需要修改核心类WeatherData,遵守ocp原则。
JDK源码中有一个ObServable类实现了观察者模式
在这里相当于 Subject这个接口,进行管理Observer
Observable,里面的addObserver,deleteObserve,notifyObserver分别对应Subject里面的相关的方法。
Observer作用等价于我们的Observer,里面也有update方法。
只是源码是通过继承实现的,Observable里面已经实现了相关的方法实现
public class Observable {
private boolean changed = false;
private Vector<Observer> obs;
/** Construct an Observable with zero Observers. */
public Observable() {
obs = new Vector<>();
}
/**
* Adds an observer to the set of observers for this object, provided
* that it is not the same as some observer already in the set.
* The order in which notifications will be delivered to multiple
* observers is not specified. See the class comment.
*
* @param o an observer to be added.
* @throws NullPointerException if the parameter o is null.
*/
public synchronized void addObserver(Observer o) {
if (o == null)
throw new NullPointerException();
if (!obs.contains(o)) {
obs.addElement(o);
}
}
/**
* Deletes an observer from the set of observers of this object.
* Passing <CODE>null</CODE> to this method will have no effect.
* @param o the observer to be deleted.
*/
public synchronized void deleteObserver(Observer o) {
obs.removeElement(o);
}
/**
* If this object has changed, as indicated by the
* <code>hasChanged</code> method, then notify all of its observers
* and then call the <code>clearChanged</code> method to
* indicate that this object has no longer changed.
* <p>
* Each observer has its <code>update</code> method called with two
* arguments: this observable object and <code>null</code>. In other
* words, this method is equivalent to:
* <blockquote><tt>
* notifyObservers(null)</tt></blockquote>
*
* @see java.util.Observable#clearChanged()
* @see java.util.Observable#hasChanged()
* @see java.util.Observer#update(java.util.Observable, java.lang.Object)
*/
public void notifyObservers() {
notifyObservers(null);
}
/**
* If this object has changed, as indicated by the
* <code>hasChanged</code> method, then notify all of its observers
* and then call the <code>clearChanged</code> method to indicate
* that this object has no longer changed.
* <p>
* Each observer has its <code>update</code> method called with two
* arguments: this observable object and the <code>arg</code> argument.
*
* @param arg any object.
* @see java.util.Observable#clearChanged()
* @see java.util.Observable#hasChanged()
* @see java.util.Observer#update(java.util.Observable, java.lang.Object)
*/
public void notifyObservers(Object arg) {
/*
* a temporary array buffer, used as a snapshot of the state of
* current Observers.
*/
Object[] arrLocal;
synchronized (this) {
/* We don't want the Observer doing callbacks into
* arbitrary code while holding its own Monitor.
* The code where we extract each Observable from
* the Vector and store the state of the Observer
* needs synchronization, but notifying observers
* does not (should not). The worst result of any
* potential race-condition here is that:
* 1) a newly-added Observer will miss a
* notification in progress
* 2) a recently unregistered Observer will be
* wrongly notified when it doesn't care
*/
if (!changed)
return;
arrLocal = obs.toArray();
clearChanged();
}
for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);
}
/**
* Clears the observer list so that this object no longer has any observers.
*/
public synchronized void deleteObservers() {
obs.removeAllElements();
}
/**
* Marks this <tt>Observable</tt> object as having been changed; the
* <tt>hasChanged</tt> method will now return <tt>true</tt>.
*/
protected synchronized void setChanged() {
changed = true;
}
/**
* Indicates that this object has no longer changed, or that it has
* already notified all of its observers of its most recent change,
* so that the <tt>hasChanged</tt> method will now return <tt>false</tt>.
* This method is called automatically by the
* <code>notifyObservers</code> methods.
*
* @see java.util.Observable#notifyObservers()
* @see java.util.Observable#notifyObservers(java.lang.Object)
*/
protected synchronized void clearChanged() {
changed = false;
}
/**
* Tests if this object has changed.
*
* @return <code>true</code> if and only if the <code>setChanged</code>
* method has been called more recently than the
* <code>clearChanged</code> method on this object;
* <code>false</code> otherwise.
* @see java.util.Observable#clearChanged()
* @see java.util.Observable#setChanged()
*/
public synchronized boolean hasChanged() {
return changed;
}
/**
* Returns the number of observers of this <tt>Observable</tt> object.
*
* @return the number of observers of this object.
*/
public synchronized int countObservers() {
return obs.size();
}
}
public interface Observer {
/**
* This method is called whenever the observed object is changed. An
* application calls an <tt>Observable</tt> object's
* <code>notifyObservers</code> method to have all the object's
* observers notified of the change.
*
* @param o the observable object.
* @param arg an argument passed to the <code>notifyObservers</code>
* method.
*/
void update(Observable o, Object arg);
}
7.工厂模式
这里讲一下比较绕的 抽象工厂模式。
使用工厂模式可以使我们客户端调用的时候变得简单,只需要修改一个工厂模式封装好的产品就行。
例子:
- 人开着car,拿着ak47,吃着Bread(面包) 对应三个类
- 魔法精灵 开着Broom(扫把),拿着魔法棒,吃着MushRoom(蘑菇) 对应三个类
3. 人是一个产品,魔法精灵也是产品要求如何更加简洁的增加其他的产品 - 可以把 形容词使用接口, 名词使用抽象类
7.1代码
Ak47
public class AK47 extends Weapon {
public void shoot(){
System.out.println("shoot----");
}
}
Car
package com.monkey.factory.abstractfactory;
import com.monkey.factory.Moveable;
public class Car extends Vehicle {
public void go(){
System.out.println("car dididid........");
}
}
Bread
package com.monkey.factory.abstractfactory;
public class Bread extends Food {
public void eat(){
System.out.println("eatBread......");
}
}
MagicStick
package com.monkey.factory.abstractfactory;
public class MagicStick extends Weapon {
public void shoot(){
System.out.println("魔法棒----");
}
}
MushRoom
package com.monkey.factory.abstractfactory;
public class MushRoom extends Food {
public void eat(){
System.out.println("mushroom-----");
}
}
Broom
package com.monkey.factory.abstractfactory;
public class Broom extends Vehicle {
public void go(){
System.out.println("魔法世界的人开broom----");
}
}
7.2抽象出来的三个抽象类
package com.monkey.factory.abstractfactory;
public abstract class Food {
abstract void eat();
}
package com.monkey.factory.abstractfactory;
public abstract class Vehicle {
abstract void go();
}
package com.monkey.factory.abstractfactory;
public abstract class Weapon {
abstract void shoot();
}
7.3 把三个抽象类放到一个抽象类中封装
package com.monkey.factory.abstractfactory;
public abstract class AbstractFactory {
abstract Food createFood();
abstract Vehicle createVehicle();
abstract Weapon createWeapon();
}
7.4 每个产品增加自己产品抽象类,继承(extends)抽象父类
7.41现代世界的人
package com.monkey.factory.abstractfactory;
public class ModernFactory extends AbstractFactory{
@Override
Food createFood() {
return new Bread();
}
@Override
Vehicle createVehicle() {
return new Car();
}
@Override
Weapon createWeapon() {
return new AK47();
}
}
7.42魔法世界的人
package com.monkey.factory.abstractfactory;
public class MagicStickFactory extends AbstractFactory {
@Override
Food createFood() {
return new MushRoom();
}
@Override
Vehicle createVehicle() {
return new Broom();
}
@Override
Weapon createWeapon() {
return new MagicStick();
}
}
7.43客户端进行调用,修改MagicStickFactory这个产品就可以进行不同的调用
package com.monkey.factory.abstractfactory;
import com.monkey.factory.Moveable;
import com.monkey.factory.Plane;
public class Main {
public static void main(String[] args) {
// new Car().go();
//
// new Plane().go();
AbstractFactory a = new MagicStickFactory();
Food food = a.createFood();
food.eat();
Weapon weapon = a.createWeapon();
weapon.shoot();
Vehicle vehicle = a.createVehicle();
vehicle.go();
}
}