面试官老问我设计模式
什么是设计模式
- 通俗来讲,设计模式是一些前人,一些大佬们从一次次的失败中总结出来的一种解决问题的方案。
- java中的23种设计模式简单的脑图

设计模式的几大原则
- 开闭原则:对扩展开放,对修改关闭。(即可新增代码,但不要修改原有的代码。)
- 里氏代换原则:继承的时候,子类可以扩展父类或者超类的功能,但是不要改父类的方法和功能等等。
- 依赖倒置原则:指面向接口编程,而不是面向实现编程
- 单一职责原则:一个方法或者一个类,尽量只负责做一个职责。
- 接口隔离原则:为系统中各个类建立它们对应的专用接口,每一个接口应该承担一种相对独立的角色,接口要做的事它就做,不要做的它就不做。
- 迪米特法则:(最少知道原则)只与你的直接朋友交谈,不和”陌生人“交谈(A->B->C,其中,A的直接朋友是B,B的直接朋友是C,A和C可理解为陌生人,此时如果A要向C通信拿数据,则可以通过B来操作,B可理解为中间件)
- 合成复用原则:它要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
常见设计模式
单例模式
只能有一个自己的对象实例,所以叫单例。
懒汉式(没加
synchronized所以线程不安全,因为它很懒,所以需要时才创建对象)
public class Lazy {
private static Lazy lazy;
//这里使用私有的构造方法,而不用public(不能被实例化)
private Lazy() {
}
public static Lazy getInstance() {
if (lazy == null) {
return new Lazy();
}
return lazy;
}
}
饿汉式(线程安全,因为它很饿,所以虚拟机启动后就马上创建对象,不管你要不要)
public class Hungry {
private static Hungry hungry = new Hungry();
//这里使用私有的构造方法,而不用public(不能被实例化)
private Hungry() {
}
public static Hungry getInstance() {
return hungry;
}
}
代理模式
现实生活中,每个地方,乡镇等火车票代售点就是官方售票的代理。我们可以不用去到很远的火车站购票,直接在家附近的代售点就可以买票回家了,方便快捷。
++代理分为静态代理和动态代理,静态代理又包括jdk动态代理和cglib动态代理。++
静态代理
购票接口
购票方法
public interface ITicket {
void ticket();
}
购票接口实现类
我要购票
public class Station implements ITicket {
@Override
public void ticket() {
System.out.println("买一张去广州的火车票");
}
}
火车站代理
我帮你代理购票
public class StationProxy implements ITicket {
private Station station;
//公有的 Station 代理构造方法,拿到 Station 订票的权利
public StationProxy(Station station) {
this.station = station;
System.out.println("我是代理点,我拿到了 Station 订票的权利,可以给你订票");
}
@Override
public void ticket() {
station.ticket();
System.out.println("购票成功");
}
}
测试
购票结果
Station station = new Station();
StationProxy stationProxy = new StationProxy(station);
stationProxy.ticket();
结果:
jdk动态代理
购票接口
public interface ITicket {
void ticket(String str);
}
购票接口实现类
public class Station implements ITicket {
@Override
public void ticket(String str) {
System.out.println("买一张去广州的火车票");
}
}
动态代理类
//实现InvocationHandler接口,重写invoke方法
public class Dynamic implements InvocationHandler {
private Object object;
public Object creatProxy(Object object) {
//将目标对象传入进行代理
this.object = object;
//将代理对象返回 //其中有三个参数
return Proxy.newProxyInstance(
object.getClass().getClassLoader(),
object.getClass().getInterfaces(),
this
);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//调用前
before();
Object invoke = method.invoke(object, args);
//调用后
after();
return invoke;
}
private void before() {
System.out.println("调用前......");
}
private void after() {
System.out.println("调用后");
}
}
测试
Dynamic dynamic = new Dynamic();
ITicket proxy = (ITicket)dynamic.creatProxy(new Station());
proxy.ticket("par");
结果:

工厂模式
!>简单工厂模式
描述花的接口
public interface IFlower {
String describe();
}
玫瑰花类
public class Rose implements IFlower{
@Override
public String describe() {
System.out.println("我是玫瑰花");
return "我是玫瑰";
}
}
百合花类
public class Lily implements IFlower {
@Override
public String describe() {
System.out.println("我是百合花");
return "我是百合";
}
}
工厂类一
public class FlowerFactory {
public static Rose creatRose(){
Rose rose = new Rose();
return rose;
}
public static Lily creatLily(){
Lily lily = new Lily();
return lily;
}
}
测试
Lily lily = FlowerFactory.creatLily();
String lilyDesc = lily.describe();
Rose rose = FlowerFactory.creatRose();
String roseDesc = rose.describe();
第二种工厂类方法,传入一个对应的花名的参数,在工厂类中作判断,然后生产出对应的花。
工厂类二
//传入一个参数,工厂制造一个对应的产品
public static IFlower create(String name){
if("lily".equals(name)){
return new Lily();
}else if("rose".equals(name)){
return new Rose();
}
return null;
}
测试
IFlower rose = FlowerFactory.create("rose");
String describe = rose.describe();
!>工厂方法模式(日常开发中常用)
前面的简单工厂模式,所有的产品都是在同一个工厂生产的,不是很友好 ,我们现在用工厂模式,分别为每个品种开一个工厂。
玫瑰工厂
public class RoseFactory {
public Rose create(){
return new Rose();
}
}
百合工厂
public class LilyFactory {
public Lily create(){
return new Lily();
}
}
测试
RoseFactory roseFactory = new RoseFactory();
Rose rose = roseFactory.create();
String roseDesc = rose.describe();
LilyFactory lilyFactory = new LilyFactory();
Lily lily = lilyFactory.create();
String lilyDesc = lily.describe();
!> 抽象工厂模式
适用于横向增加同类工厂,不适合新增功能这样的纵向扩展。
总工厂接口
public interface IFactory {
IFlower instance();
}
玫瑰工厂
//玫瑰工厂实现总工厂接口
public class RoseFactory implements IFactory {
@Override
public IFlower instance(){
return new Rose();
}
}
百合工厂
//百合工厂实现总工厂接口
public class LilyFactory implements IFactory {
@Override
public IFlower instance() {
return new Lily();
}
}
测试
IFactory roseFactory = new RoseFactory();
IFlower rose = roseFactory.instance();
String roseDesc = rose.describe();
IFactory lilyFactory = new LilyFactory();
IFlower lily = lilyFactory.instance();
String lilyDesc = lily.describe();
策略模式
适用于策略少于等于4个的时候,太多策略会造成策略膨胀。
数学加减法接口
public interface IMath {
public int addOrSub(int a, int b);
}
加法类
//加法类实现IMath接口
public class Add implements IMath {
@Override
public int addOrSub(int a, int b) {
return a + b;
}
}
减法类
//减法类实现IMath接口
public class Sub implements IMath {
@Override
public int addOrSub(int a, int b) {
return a - b;
}
}
计算类
public class Calculated {
private IMath iMath;
public Calculated(IMath iMath) {
this.iMath = iMath;
}
//开始计算
public int startCal(int a, int b) {
return iMath.addOrSub(a, b);
}
}
测试
//构造计算类时候,通过传递的参数确定是加法还是减法
Calculated addCal = new Calculated(new Add());
int add = addCal.startCal(3, 2);
Calculated subCal = new Calculated(new Sub());
int sub = subCal.startCal(1, 10);
观察者模式
观察者模式和发布订阅模式有点类似,区别在于前者只有两个对象(观察者和被观察者),后者有三个对象(发布者、Broker和订阅者),多了一个中转站。
监听消息接口
public interface IListener {
void onListen(String message);
}
观察者1
public class LaoWang implements IListener {
@Override
public void onListen(String message){
System.out.println("我是隔壁老王,收到了Observe发布的消息:"+"{"+message+"}");
}
}
观察者2
public class LaoWu implements IListener {
@Override
public void onListen(String message) {
System.out.println("我是隔壁老吴,收到了Observe发布的消息:" + "{" + message + "}");
}
}
被观察者
public class Observe {
List<IListener> listeners = new ArrayList<IListener>();
//通过传入对应的订阅者,开始订阅Observe
public void addListener(IListener iListener) {
listeners.add(iListener);
}
//Observe向观察者集合listeners发消息
public void sendMessage() {
listeners.forEach(obj -> {
obj.onListen("我是Observe推送过来的消息体");
});
}
}
测试
Observe observe = new Observe();
//老王订阅Observe
Observe observe.addListener(new LaoWang());
//Observe向订阅者发消息
observe.sendMessage();
结果:
//控制台打印:
//我是隔壁老王,收到了Observe发布的消息:{我是Observe推送过来的消息体}
装饰者模式
适配器模式
定义:把一个类的接口变换成客户端所期待的另一种接口,使得原本因接口不匹配而无法在一起工作的两个类可以一起工作,即系统只有220伏的接口,我现在要用110伏,所以适配器就产生了,通过适配器把220转成110伏。
角色组成:源角色=>适配器=>目标角色
- 类适配器模式
中国标准电压类
public class HighVoltage {
public int chinaVoltage() {
int voltage = 220;
return voltage;
}
}
高转低电压接口
public interface IHighToLow {
int change();
}
类适配器
//继承源角色并实现目标角色
public class Adapter extends HighVoltage implements IHighToLow {
@Override
public int change() {
//获取中国标准电压
int china = chinaVoltage();
//电压转换
int change = china / 2;
System.out.println("转换后的电压是:" + change + "伏");
return change;
}
}
测试
//输出:转换后的电压是:110伏
- 对象适配器模式
对象适配器类
适配器实现转电压接口,重写转换方法。构造适配时,传入一个HighVoltage对象。
public class ObjAdapter implements IHighToLow {
private HighVoltage highVoltage;
public ObjAdapter(HighVoltage highVoltage) {
this.highVoltage = highVoltage;
}
@Override
public int change() {
//获取中国标准电压
int china = highVoltage.chinaVoltage();
//电压转换
int change = china / 2;
System.out.println("转换后的电压是:" + change + "伏");
return change;
}
}
Template模板模式
例如:RedisTemplate、JdbcTemplate等模板,可封装不变部分,扩展可变部分,提取公共代码,便于维护,行为由父类控制,子类实现。
游戏抽象类
//游戏抽象类,不能实例化new,只能由子类实现行为
public abstract class Game {
abstract void init();
abstract void start();
abstract void end();
//因模板是固定不变的,所以用final修饰
public final void play() {
init();
start();
end();
}
}
玩王者模板类
public class WangZhe extends Game {
@Override
void init() {
System.out.println("初始化王者");
}
@Override
void start() {
System.out.println("我开始打王者");
}
@Override
void end() {
System.out.println("我退出了王者");
}
}
玩吃鸡模板类
public class ChiJi extends Game {
@Override
public void init(){
System.out.println("初始化吃鸡");
}
@Override
public void start(){
System.out.println("我开始玩吃鸡");
}
@Override
public void end(){
System.out.println("我退出了吃鸡");
}
}
测试
Game wangZhe = new WangZhe();
wangZhe.play();
//控制台打印结果:
//初始化王者
//我开始打王者
//我退出了王者
由测试结果可知,模板模式常用来解决固定的一系列操作,比如jdbc连接数据库的步骤分为:1.加载驱动 =》2.创建连接 =》3.预处理 =》4.执行sql语句 =》5.关闭jdbc连接流

浙公网安备 33010602011771号