当前位置 博文首页 > 程序员springmeng:??设计模式肝完了,还挺全!腾讯和阿里的offe
设计模式肝完了,还挺全!腾讯和阿里的offer已拿!
金九银十已经来了,挺近大厂最好的机会已经来了!如果你是要找工作的,一定要抓住这个机会!
前面已经整理了很多的面试资料:
1,??爆肝!整理了一周的Spring面试大全【含答案】,吊打Java面试官【建议收藏】!??
2,??肝完了,一天掌握数据结构和算法面试题,吊打面试官,建议收藏??
3,??集合很简单?开什么玩笑?肝了一周,全是精华,万字讲解!面试再不怕集合问题了!!!??
4,肝完了,总结了SpringBoot与缓存的知识点,快速掌握
5,Mysql面试大全,看完可以吊打面试官!!!
6,入计算机专业的师弟师妹,别再迷茫了,我整理一份CS的学习路线大全!帮你超越大部分的同学!
7,??专科出身拿到阿里offer,小孟直呼666!【付硬核面试】??
前面一个小伙伴准备非常充分,已经拿到了阿里的和腾讯的offer,通过和他聊面经,我发现设计模式被面的概率基本是100%。
?
?上次投票,在大厂的同学挺多啊,欢迎给我面经,质量搞,我可以付费,为价值买单,帮助你我他!
废话多不多,直接开干!
目录
Q1:设计模式有多少种?
Q2:设计模式有哪些原则?
Q3:设计模式的分类,你知道哪些设计模式?
Q4:说?说简单??模式
Q5:说?说???法模式
Q6:Spring 框架中都用到了哪些设计模式?
Q7:抽象??模式了解吗?
Q8:单例模式的特点是什么?
Q9:单例模式有哪些实现?
Q10:讲?讲代理模式
Q11:讲?讲装饰器模式
Q12:装饰器模式和动态代理的区别?
Q13:讲?讲适配器模式
Q14:适配器模式和和装饰器模式以及代理模式的区别?
Q15:讲?讲策略模式
Q16:讲?讲模板模式
Q17:讲?讲观察者模式
有的时候会问设计模式基本的概念,有的时候会问设计模式具体的内容或者让你手画图,有的时候会问框架用到了那些设计模式!
设计模式一般分为23种。
创建型模式:
工厂方法模式(Factory Method)
抽象工厂模式(Abstract Factory)
单例模式(Singleton)
建造者模式(Builder)
原型模式(Prototype)
结构型模式:
适配器模式(Adapter)
装饰器模式(Decorator)
代理模式(Proxy)
外观模式(Facade)
桥接模式(Bridge)
组合模式(Composite)
享元模式(Flyweight)
行为型模式:
策略模式(Strategy)
模板方法模式(Template Method)
观察者模式(Observer)
迭代器模式(Iterator)
责任链模式(Chain of Responsibility)
命令模式(Command)
备忘录模式(Memento)
状态模式(State)
访问者模式(Visitor)
中介者模式(Mediator)
解释器模式(Interpreter)
通常来说设计模式的原则有依赖导致原则、单一职责原则、接口隔离原则、开闭原则、迪米特法原则、里氏替换原则。设计模式原则图如下所示:
创建型: 在创建对象的同时隐藏创建逻辑,不使? new 直接实例化对象,程序在判断需要创建哪些对象时更灵活。包括工厂/抽象工厂/单例/建造者/原型模式。
结构型: 通过类和接?间的继承和引?实现创建复杂结构的对象。包括适配器/桥接模式/过滤器/组合/ 装饰器/外观/享元/代理模式。
行为型: 通过类之间不同通信方式实现不同行为。包括责任链/命名/解释器/迭代器/中介者/备忘录/观察者/状态/策略/模板/访问者模式。
分类图如下所示:
?
简单工厂模式指由?个工厂对象来创建实例,客户端不需要关注创建逻辑,只需提供传?工厂的参数。
适?于工厂类负责创建对象较少的情况,缺点是如果要增加新产品,就需要修改工厂类的判断逻辑,违背开闭原则,且产品多的话会使工厂类?较复杂。
Calendar 抽象类的?历对象。方法,调?方法根据不同的地区参数创建不同的Spring 中的 BeanFactory 使?简单工厂模式,根据传??个唯?的标识来获得 Bean 对象。
简单的实例:
/**
?* 拿铁、美式咖啡、卡布奇诺等均为咖啡家族的一种产品
?* 咖啡则作为一种抽象概念
?* @author
?*/
public abstract class Coffee {
??? /**
???? * 获取coffee名称
???? * @return
???? */
??? public abstract String getName();
}
/**
?* 美式咖啡
?* @author Lsj
?*
?*/
public class Americano extends Coffee {
??? @Override
??? public String getName() {
??????? return "美式咖啡";
??? }
}
/**
?* 卡布奇诺
?* @author Lsj
?*/
public class Cappuccino extends Coffee {
??? @Override
??? public String getName() {
??????? return "卡布奇诺";
??? }
}
/**
?* 拿铁
?* @author Lsj
?*/
public class Latte extends Coffee {
??? @Override
??? public String getName() {
??????? return "拿铁";
??? }
}
工厂方法模式指定义?个创建对象的接?,让接?的实现类决定创建哪种对象,让类的实例化推迟到?类中进行。
客户端只需关?对应工厂而无需关?创建细节,主要解决了产品扩展的问题,在简单??模式中如果产品种类变多,工厂的职责会越来越多,不便于维护。
Collection 接?这个抽象工厂中定义了?个抽象的 工厂方法,返回?个 Iterator 类的抽象产
品。该?法通过 ArrayList 、HashMap 等具体工厂实现,返回 Itr、KeyIterator 等具体产品。
Spring 的 FactoryBean 接?的?法也是工厂方法。
Spring 框架中使用到了大量的设计模式,下面列举了比较有代表性的:
1. 代理模式—在 AOP 和 remoting 中被用的比较多。
2. 单例模式—在 spring 配置文件中定义的 bean 默认为单例模式。
3. 前端控制器—Spring 提供了 DispatcherServlet 来对请求进行分发。
抽象工厂模式指提供?个创建?系列相关或相互依赖对象的接?,?需指定它们的具体类。
客户端不依赖于产品类实例如何被创建和实现的细节,主要?于系统的产品有多于?个的产品族,?系 统只消费其中某?个产品族产品的情况。抽象工厂模式的缺点是不方便扩展产品族,并且增加了系统的 抽象性和理解难度。
java.sql.Connection 接?就是?个抽象工厂,其中包括很多抽象产品如 Statement、Blob、Savepoint等。
public class AbstractFactoryTest {
?? public static void main(String[] args) {
?????? // 抽象工厂
?????? String result = (new CoffeeFactory()).createProduct("Latte");
?????? System.out.println(result); // output:拿铁
?? }
}
// 抽象工厂
abstract class AbstractFactory{
?? public abstract String createProduct(String product);
}
// 啤酒工厂
class BeerFactory extends AbstractFactory{
?? @Override
?? public String createProduct(String product) {
?????? String result = null;
?????? switch (product) {
?????????? case "Hans":
?????????????? result = "汉斯";
?????????????? break;
?????????? case "Yanjing":
?????????????? result = "燕京";
?????????????? break;
?????????? default:
?????????????? result = "其他啤酒";
?????????????? break;
?????? }
?????? return result;
?? }
}
/\* \* 咖啡工厂 \*/
class CoffeeFactory extends AbstractFactory{
?? @Override
?? public String createProduct(String product) {
?????? String result = null;
?????? switch (product) {
?????????? case "Mocca":
?????????????? result = "摩卡";
?????????????? break;
?????????? case "Latte":
?????????????? result = "拿铁";
????? ?????????break;
?????????? default:
?????????????? result = "其他咖啡";
?????????????? break;
?????? }
?????? return result;
?? }
}
单例模式属于创建型模式,?个单例类在任何情况下都只存在?个实例,构造方法必须是私有的、由自己创建?个静态变量存储实例,对外提供?个静态公有方法获取实例。
优点是内存中只有?个实例,减少了开销,尤其是频繁创建和销毁实例的情况下并且可以避免对资源的多重用。缺点是没有抽象层,难以扩展,与单?职责原则冲突。
Spring 的 ApplicationContext 创建的 Bean 实例都是单例对象,还有 ServletContext、数据库连接池等也都是单例模式。
UML类图如下所示:
?
单例模式举例实现:
1 class Person{
?2 private:
?3???? Person(){};
?4???? Person(const Person&){};
?5
?6???? static Person *Singleton; //静态变量,类内声明,类外初始化, 初始化时不用static,需指定作用域
?7
?8 public:
?9???? static Person *getInstance(){
10???????? return Singleton;
11???? }
12 };
13
14 Person *Person::Singleton = new Person;
15
16 void test01(){
17
18???? Person::getInstance();//外部通过接口访问单例
19???? Person *p = Person::getInstance();
20???? Person *p1 = Person::getInstance();
21
22???? //--------------
23???? //不能直接定义或new实例,只能通过调用接口
24???? //Person p2;
25???? //Person *p1 = new(*p); //这里调用拷贝构造函数
26???? //--------------
27
28???? if (p == p1){
29???????? cout << "两个单例的地址相同,为同一个单例" << endl;
30???? }
31
32 }
饿汉式:在类加载时就初始化创建单例对象,线程安全,但不管是否使?都创建对象可能会浪费内存。
懒汉式:在外部调?时才会加载,线程不安全,可以加锁保证线程安全但效率低。
双重检查锁:使? volatile 以及多重检查来减?锁范围,提升效率。
静态内部类:同时解决饿汉式的内存浪费问题和懒汉式的线程安全问题。
枚举:《E?ective Java》提倡的?式,不仅能避免线程安全问题,还能防?反序列化重新创建新的对象,绝对防?多次实例化,也能防?反射破解单例的问题。
代理模式属于结构型模式,为其他对象提供?种代理以控制对这个对象的访问。优点是可以增强?标对 象的功能,降低代码耦合度,扩展性好。缺点是在客户端和?标对象之间增加代理对象会导致请求处理速度变慢,增加系统复杂度。
为其他对象提供一种代理以控制对这个对象的访问。在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
具体的代理模式的代码如下所示:
package com.Model.Proxy;
//买车行为
public interface BuyCar {
??? public void buycar();
}
package com.Model.Proxy;
public class People implements BuyCar {
??? private int cash;
??? private String vip;
??? private String username;
??? @Override
??? public void buycar() {
??????? System.out.println(username + " is vip so he/she can buy any car...");
??? }
??? public int getCash() {
??????? return cash;
??? }
??? public void setCash(int cash) {
??????? this.cash = cash;
??? }
??? public String getVip() {
??????? return vip;
??? }
??? public void setVip(String vip) {
??????? this.vip = vip;
??? }
??? public String getUsername() {
??????? return username;
??? }
??? public void setUsername(String username) {
??????? this.username = username;
??? }
}
// 代理类 去检测买车行为是否符合规则
class ProxyBuyCar implements BuyCar {
??? private People People;
??? public People getPeople() {
??????? return People;
??? }
??? public void setPeople(People people) {
??????? People = people;
??? }
??? @Override
??? public void buycar() {
??????? if (People.getVip().equals("vip")) {
??????????? People.buycar();
??????? } else if (People.getCash() >= 50000) {
??????????? System.out.println(People.getUsername() + "buy a new car trade over...");
??????? } else {
??????????? System.out.println(People.getUsername() + "people can't buy a car ");
??????? }
??? }
}
Spring 利?动态代理实现 AOP,如果 Bean 实现了接?就使? JDK 代理,否则使? CGLib 代理。
静态代理:代理对象持有被代理对象的引?,调?代理对象?法时也会调?被代理对象的?法,但是会 在被代理对象?法的前后增加其他逻辑。需要?动完成,在程序运?前就已经存在代理类的字节码?件,代理类和被代理类的关系在运?前就已经确定了。 缺点是?个代理类只能为?个?标服务,如果要服务多种类型会增加?作量。
动态代理:动态代理在程序运?时通过反射创建具体的代理类,代理类和被代理类的关系在运?前是不确定的。动态代理的适?性更强,主要分为 JDK 动态代理和 CGLib 动态代理。
JDK 动态代理:通过类的?法获取?个动态代理对象,需要传?三个参数,被代理对象的类加载器、被代理对象实现的接?,以及?个器来指明具体的逻辑,相?静态代理的优势是接?中声明的所有?法都被转移到调?处理的?法集中处理。
CGLib 动态代理:JDK 动态代理要求实现被代理对象的接?,? CGLib 要求继承被代理对象,如果?个类是 ?nal 类则不能使? CGLib 代理。两种代理都在运?期?成字节码,JDK 动态代理直接写字节码,? CGLib 动态代理使? ASM 框架写字节码,ASM 的?的是?成、转换和分析以字节数组表示的已编译 Java 类。 JDK 动态代理调?代理?法通过反射机制实现,? GCLib 动态代理通过 FastClass 机制直接调??法,它为代理类和被代理类各?成?个类,该类为代理类和被代理类的?法分配?个 int 参数,调??法时可以直接定位,因此调?效率更?。
我们首先看下模式的结构图:
装饰器模式属于结构型模式,在不改变原有对象的基础上将功能附加到对象,相?继承可以更加灵活地 扩展原有对象的功能。
装饰器模式适合的场景:在不想增加很多?类的前提下扩展?个类的功能。
java.io 包中,InputStream 字节输?流通过装饰器 Bu?eredInputStream 增强为缓冲字节输?流。
首先我们看一下他们的类图:
?
装饰器模式的关注点在于给对象动态添加?法,?动态代理更注重对象的访问控制。动态代理通常会在 代理类中创建被代理对象的实例,?装饰器模式会将装饰者作为构造?法的参数。
适配器模式属于结构型模式,它作为两个不兼容接?之间的桥梁,结合了两个独?接?的功能,将?个 类的接?转换成另外?个接?使得原本由于接?不兼容?不能?起?作的类可以?起?作。
缺点是过多使?适配器会让系统?常混乱,不易整体把握。
java.io 包中,InputStream 字节输?流通过适配器 InputStreamReader 转换为 Reader 字符输?流。
Spring MVC 中的 HandlerAdapter,由于 handler 有很多种形式,包括 Controller、HttpRequestHandler、Servlet 等,但调用方式?是确定的,因此需要适配器来进?处理,根据适配规则调? handle 方法。
Arrays.asList ?法,将数组转换为对应的集合(注意不能使?修改集合的?法,因为返回的 ArrayList是 Arrays 的?个内部类)。
适配器实现的代码如下:
/\* \* 传统的充电线 MicroUSB \*/
interface MicroUSB {
??? void charger();
}
/\* \* TypeC 充电口 \*/
interface ITypeC {
??? void charger();
}
class TypeC implements ITypeC {
? ??@Override
??? public void charger() {
??????? System.out.println("TypeC 充电");
??? }
}
/\* \* 适配器 \*/
class AdapterMicroUSB implements MicroUSB {
??? private TypeC typeC;
??? public AdapterMicroUSB(TypeC typeC) {
??????? this.typeC = typeC;
??? }
??? @Override
??? public void charger() {
??????? typeC.charger();
??? }
}
/\* \* 测试调用 \*/
public class AdapterTest {
??? public static void main(String[] args) {
??????? TypeC typeC = new TypeC();
??????? MicroUSB microUSB = new AdapterMicroUSB(typeC);
? ??????microUSB.charger();
??? }
}
适配器模式没有层级关系,适配器和被适配者没有必然连续,满? has-a 的关系,解决不兼容的问题, 是?种后置考虑。
装饰器模式具有层级关系,装饰器与被装饰者实现同?个接?,满? is-a 的关系,注重覆盖和扩展,是?种前置考虑。
适配器模式主要改变所考虑对象的接?,?代理模式不能改变所代理类的接?。
策略模式属行为模型模式,定义了?系列算法并封装起来,之间可以互相替换。策略模式主要解决在有多种算法相似的情况下,使? if/else 所带来的难以维护。
优点是算法可以?由切换,可以避免使用多重条件判断并且扩展性良好,缺点是策略类会增多并且所有 策略类都需要对外暴露。
在集合框架中,经常需要通过构造方法传??个比较器 Comparator 进行比较排序。Comparator 就是
?个抽象策略,?个类通过实现该接?并重写 compare 方法成为具体策略类。
创建线程池时,需要传?拒绝策略,当创建新线程使当前运?的线程数超过 maximumPoolSize 时会使?相应的拒绝策略处理。
策略模式实现的代码如下所示:
/\* \* 声明旅行 \*/
interface ITrip {
??? void going();
}
class Bike implements ITrip {
??? @Override
??? public void going() {
??????? System.out.println("骑自行车");
??? }
}
class Drive implements ITrip {
??? @Override
??? public void going() {
??????? System.out.println("开车");
??? }
}
/\* \* 定义出行类 \*/
class Trip {
??? private ITrip trip;
??? public Trip(ITrip trip) {
??????? this.trip = trip;
??? }
??? public void doTrip() {
??????? this.trip.going();
??? }
}
/\* \* 执行方法 \*/
public class StrategyTest {
??? public static void main(String[] args) {
??????? Trip trip = new Trip(new Bike());
??????? trip.doTrip();
??? }
}
程序执行的结果: 骑自行车
模板模式属于?为型模式,使?类可以在不改变算法结构的情况下重新定义算法的某些步骤,适?于抽 取?类重复代码到公共?类。
优点是可以封装固定不变的部分,扩展可变的部分。缺点是每?个不同实现都需要?个?类维护,会增 加类的数量。
为防?恶意操作,?般模板?法都以 ?nal 修饰。
HttpServlet 定义了?套处理 HTTP 请求的模板,service ?法为模板?法,定义了处理HTTP请求的基本流程,doXXX 等?法为基本?法,根据请求?法的类型做相应的处理,?类可重写这些?法。