开闭原则:对扩展开放,对修改关闭
里氏替换原则:继承必须确保超类所拥有的性质在子类中仍然成立
依赖倒置原则:要面向接口编程,不要面向实现编程。
单一职责原则:控制类的粒度大小,将对象解耦、提高其内聚性(一个方法最好干一件事情)
接口隔离原则:要为各个类建立它们需要的专用接口
迪米特法则:只与你的直接朋友交谈,不跟”陌生人“说话。
合成复用原则:尽量先使用组合或聚合等关联关系来实现,其次才考虑使用继承关系来实现。
面试常见的设计模式:单例模式、工厂方法模式、抽象工厂模式、代理模式、职责链模式
作用
- 实现了创建者和调用者的分离
核心
- 实例化对象不使用new,用工厂方法代替
- 将选择实现类,创建对象统一管理和控制。从而将调用者跟我们的实现类解耦
分类
- 简单工厂模式(静态工厂模式)
- 用来生产同一等级结构中的任意产品(对于增加新的产品,需要覆盖已有代码)
- 虽然某种程度上不符合设计原则,但实际使用最多
- 一个工厂生产一个产品
- 工厂方法模式
- 用来生产同一等级结构中的固定产品(支持增加任意产品)
- 不修改已有类的前提下,通过增加新的工厂类实现扩展
- 一个工厂生产一个产品
- 抽象工厂模式
- 围绕一个超级工厂创建其他工厂。该超级工程又称为其他工厂的工厂
- 不可以增加产品(如电脑),但是可以增加产品族(如苹果)
- 一个工厂生产一个产品族
应用场景
- JDK中Calendar的getInstance方法
- JDBC中的Connection对象获取
- Spring中IOC容器创建管理bean对象
- 反射中Class对象的newInstance方法
优缺点
| 结构复杂度 | 代码复杂度 | 编程复杂度 | 管理复杂度 | 扩展性 | |
|---|---|---|---|---|---|
| Simple | 高 | ||||
| method | 低 | 低 | 低 | 低 | 低 |
根据设计模式:工厂方法模式
根据实际业务:简单工厂模式
代码
- 简单工厂模式(一个工厂):
public class CarFactory {
// 静态(简单)工厂模式,增加车型要修改代码,不符合开闭原则。
public static Car getCar(String car){
if(car.equals("Tesla")){
return new Tesla();
}else if(car.equals("Porsche")){
return new Porsche();
}else{
return null;
}
}
}- 工厂方法模式(多个工厂):
// 工厂方法模式:
// 添加新车型不需要该工厂类
// 可以动态扩展车型:创建对应的工厂
public interface CarFactory {
Car getCar();
}public class TeslaFactory implements CarFactory{
@Override
public Car getCar() {
return new Tesla();
}
}- 抽象工厂模式():
定义
- 抽象工厂模式提供了一个创建一系列相关或者相互依赖对象的接口,无需指定它们具体的类
应用场景
- 客户端(应用层)不依赖于产品类实例如何被创建、实现等细节
- 强调一系列相关的产品对象(属同一产品族)一起使用创建对象需要大量的重复代码
- 提供一个产品类的库,所有的产品以相同的接口出现,从而使得客户端不依赖具体的实现
优点
- 具体产品在应用层的代码隔离,无需关心创建细节
- 将一个系列的产品统一到一起创建
缺点
- 规定了所有可能被创建的产品集合,产品簇中扩展新产品困难
- 增加了系统的抽象性和理解难度
代码
public interface Phone {
void start();
void shutdown();
void call();
void sendSMS();
}public interface Router {
void start();
void shutdown();
void setting();
}// 抽象工厂:抽象的抽象
// 新增产品:直接往接口添加就行,但是非常麻烦
public interface Factory {
Phone phoneProduct();
Router routerProduct();
}定义
- 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
作用
- 在用户不知道 对象的构造过程和细节 的情况下就可以直接创建复杂的对象
- 用户只需要给出指定复杂对象的类型和内容,建造者模式复制按顺序创建复杂的对象(把内部构造过程和细节隐藏)
应用场景
- 工厂(建造者模式):负责制造汽车(组装过程和细节在工厂内)
- 汽车购买者(用户):只负责指定型号(对象的类型和内容),然后直接购买就可以使用(不需要直到内部如何组装)
- 工厂模式构造零件,建造者模式将零件构造为复杂的对象
角色分析
优缺点
与工厂模式区别
代码
public class Product {
private String A;
private String B;
private String C;
// 省略get、set、toString方法
}// 抽象的建造者:方法
public abstract class Builder {
// 构造产品
abstract void buildA();
abstract void buildB();
abstract void buildC();
// 得到产品
abstract Product getProduct();
}// 工人,负责构造
public class Worker extends Builder{
private Product product;
public Worker(){
product=new Product();
}
@Override
void buildA() {
product.setA("A");
}
@Override
void buildB() {
product.setB("B");
}
@Override
void buildC() {
product.setC("C");
}
@Override
Product getProduct() {
return product;
}
}// 指挥:核心。负责指挥构建一个产品。
public class Director {
// 指挥Worker。按照顺序构建
public Product build(Builder builder){
builder.buildA();
builder.buildB();
builder.buildC();
return builder.getProduct();
}
}public class Test {
public static void main(String[] args) {
// 指挥
Director director=new Director();
// 指挥工人构建:可以实现不同的工人后,指挥不同的工人构造不同产品
Product product=director.build(new Worker());
// 不过,一般我们可以通过静态内部类实现零件无序装配构造,这种方式更灵活
}
}public class Product {
private String A="";
private String B="";
private String C="";
// 省略get、set、toString方法
}// 抽象的建造者:方法
public abstract class Builder {
// 构造产品
abstract Builder buildA(String a);
abstract Builder buildB(String b);
abstract Builder buildC(String c);
// 得到产品
abstract Product getProduct();
}public class Worker extends Builder{
private Product product;
@Override
Builder buildA(String a) {
product.setA(a);
return this;
}
@Override
Builder buildB(String b) {
product.setB(b);
return this;
}
@Override
Builder buildC(String c) {
product.setC(c);
return this;
}
@Override
Product getProduct() {
return product;
}
}public class test {
public static void main(String[] args) {
// 工人负责创建
Worker worker=new Worker();
// 链式编程
Product product=worker.buildA("a").buildB("b").buildC("c").getProduct();
System.out.println(product);
}
}为另一个对象提供一个替身或占位符以控制对这个对象的访问
-
目的
控制对其他对象的访问
-
类型(4种)
- 远程代理(Remote Proxy):控制对远程对象(不同地址空间)的访问,它负责将请求及参数进行编码,并向不同地址空间中的对象发送已经编码的请求。
- 虚拟代理(Virtual Proxy):根据需要创建开销很大的对象,它可以缓存实体的附加信息,以便延迟对它的访问,例如在网站加载一个很大的图片时,不能马上完成,可以用虚拟代理缓存图片的大小信息,然后生成一张临时图片代替原始图片
- 保护代理(Protection Proxy):按权限控制对象的访问,它负责检查调用者是否具有实现一个请求所必须的访问权限。
- 智能代理(Smart Reference):取代了简单的指针,他在访问对象时执行一些附加操作:记录对象的引用次数;当第一次引用一个持久化对象时,将它装入内存;访问一个实际对象前,检查是否已经锁定了它,以确保其他对象不能改变它
-
实现
以下是一个虚拟代理的实现,模拟了图片延迟加载的情况下使用与图片大小相等的临时内容去替换原始图片,直到图片加载完成才将图片显示出来。
public interface Image { void showImage(); }
public class HighResolutionImage implements Image { private URL imageURL; private long startTime; private int height; private int width; public int getHeight() { return height; } public int getWidth() { return width; } public HighResolutionImage(URL imageURL) { this.imageURL = imageURL; this.startTime = System.currentTimeMillis(); this.width = 600; this.height = 600; } public boolean isLoad() { // 模拟图片加载,延迟 3s 加载完成 long endTime = System.currentTimeMillis(); return endTime - startTime > 3000; } @Override public void showImage() { System.out.println("Real Image: " + imageURL); } }
public class ImageProxy implements Image { private HighResolutionImage highResolutionImage; public ImageProxy(HighResolutionImage highResolutionImage) { this.highResolutionImage = highResolutionImage; } @Override public void showImage() { while (!highResolutionImage.isLoad()) { try { System.out.println("Temp Image: " + highResolutionImage.getWidth() + " " + highResolutionImage.getHeight()); Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } highResolutionImage.showImage(); } }
public class ImageViewer { public static void main(String[] args) throws Exception { String image = "http://image.jpg"; URL url = new URL(image); HighResolutionImage highResolutionImage = new HighResolutionImage(url); ImageProxy imageProxy = new ImageProxy(highResolutionImage); imageProxy.showImage(); } }
-
JDK
- java.lang.Proxy
- RMI
-
目的
定义算法族,分别封装起来,之间可以相互替换,让算法的变化独立于使用算法的客户端
-
类型
- Strategy接口定义了一个算法族,它们都具有behavior()方法
- Content是使用该算法族的类,其中的doSomething()方法会调用behavior()方法,setStrategy(int Strategy)方法可以动态地改变strategy对象,也就是说能动态地改变Content所使用地算法
-
与状态模式的比较
状态模式的类图和策略模式类似,并且都是能够动态改变对象的行为。但是状态模式是通过状态转移来改变 Context 所组合的 State 对象,而策略模式是通过 Context 本身的决策来改变组合的 Strategy 对象。所谓的状态转移,是指 Context 在运行过程中由于一些条件发生改变而使得 State 对象发生改变,注意必须要是在运行过程中。
状态模式主要是用来解决状态转移的问题,当状态发生转移了,那么 Context 对象就会改变它的行为;而策略模式主要是用来封装一组可以互相替代的算法族,并且可以根据需要动态地去替换 Context 使用的算法
-
实现
设计一个鸭子,它可以动态地改变叫声。这里的算法族是鸭子的叫声行为。
public interface QuackBehavior { void quack(); }
public class Quack implements QuackBehavior { @Override public void quack() { System.out.println("quack!"); } }
public class Squeak implements QuackBehavior{ @Override public void quack() { System.out.println("squeak!"); } }
public class Duck { private QuackBehavior quackBehavior; public void performQuack() { if (quackBehavior != null) { quackBehavior.quack(); } } public void setQuackBehavior(QuackBehavior quackBehavior) { this.quackBehavior = quackBehavior; } }
public class Client { public static void main(String[] args) { Duck duck = new Duck(); duck.setQuackBehavior(new Squeak()); duck.performQuack(); duck.setQuackBehavior(new Quack()); duck.performQuack(); } }
squeak! quack!
-
JDK
- java.util.Comparator#compare()
- javax.servlet.http.HttpServlet
- javax.servlet.Filter#doFilter()
在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤