面向对象设计的六大设计原则
1、单一职责原则(Single Responsibility Principle, SRP)
:一个类应该,完整负责且只负责一个领域。完整负责(高内聚)以减少零散的类、只负责(低耦合)以方便被复用
2、接口隔离原则(Interface Segregation Principle, ISP)
:即接口的单一职责原则
3、迪米特法则(Law of Demeter, LoD)
:一个类不要直接去访问其对象成员的对象成员,可以通过其对象成员间接访问。使得,修改一个类时,只涉及直接使用它的类,而不涉及间接使用它的类,以降低耦合度
4、开闭原则(Open-Closed Principle, OCP)
:一个类应该,对扩展开放,对修改关闭。即在不被修改的情况下也能被扩展
5、里氏代换原则(Liskov Substitution Principle, LSP)
:一个基类声明的变量,必须也能引用其子类的对象,反过来则不行。这是JAVA语言内置特性,即子类对象可以转换为父类,反过来则不行
6、依赖倒转原则(Dependency Inversion Principle, DIP)
:尽量用抽象类声明变量,具体类中尽量只实现抽象类中有声明的方法。使得,在新增具体类时,不用修改已有的可能引用它的地方,且它的方法都能被访问到
六大设计原则之间的关系:
单一职责原则、接口隔离原则、迪米特法则都是为了低耦合
里氏代换原则是实现开闭原则的基础,依赖倒转原则是实现开闭原则的一个基本手段
解耦的两种形式:单一职责原则(把一个整体拆分成相对独立的模块),迪米特法则
模式分类
创建型模式,5种:单例模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式
结构型模式,7种:适配器模式、桥接模式、组合模式、装饰器模式、外观模式(门面模式)、享元模式、代理模式
行为型模式,11种:策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式
优缺点分析
复杂度,代码复用,解耦,开闭原则
1、单例模式(Singleton)
懒汉式
public class Singleton {
private static Singleton instance=null;
private Singleton(){ }
public static Singleton getInstance(){
if(instance==null){ //这里线程不安全
instance=new Singleton();
}
return instance;
}
}
饿汉式
public class Singleton {
private static Singleton instance=new Singleton(); //这里超前加载了
private Singleton(){ }
public static Singleton getInstance(){
return instance;
}
}
内部类式
public class Singleton {
private Singleton(){ }
private static class SingletonHolder{
private final static Singleton instance=new Singleton();
}
//调用getInstance,才会去加载内部类的 static 成员,实现了延迟加载
public static Singleton getInstance(){
return SingletonHolder.instance;
}
}
理解:只产生一个实例,节约资源,只要上一个对象锁就能保证原子性
JDK案例:Runtime.getRuntime();NumberFormat.getInstance();
2、工厂方法模式
简单工厂模式(静态工厂方法模式,Simple Factory)
public class Factory{
public static Sample creator(int which){
if (which==1)
return new SampleA();
else if (which==2)
return new SampleB();
}
}
理解:一个工厂的同一个方法,生产不同产品(限共同父类)
JDK案例:Integer.valueOf(); Calendar.getInstance();java.lang.Class.forName()
工厂方法模式(Factory Method)
抽象产品,具体产品A,具体产品B,抽象工厂,具体工厂A,具体工厂B
使用:
抽象工厂 factory = new 具体工厂A();
抽象产品 m = factory.create();
理解:不同工厂的同一个方法,生产不同产品(限共同父类)。新增具体产品时,新增工厂,不用去改已有的工厂,符合开闭原则。
JDK案例:1、抽象工厂方法 java.util.Collection.iterator(),抽象产品 java.util.Iterator
2、java.lang.Object#toString()
3、抽象工厂模式(Abstract Factory)
// 具体工厂类,其中Food,Vehicle,Weapon是抽象产品
public class DefaultFactory extends AbstractFactory{
public Food createFood() {
return new Apple();
}
public Vehicle createVehicle() {
return new Car();
}
public Weapon createWeapon() {
return new AK47();
}
}
使用:
AbstractFactory f = new DefaultFactory();
Vehicle v = f.createVehicle();
Weapon w = f.createWeapon();
Food food = f.createFood();
理解:同一个工厂的不同方法,生产不同产品(不限共同父类)
JDK实例: java.sql.DriverManager.getConnection()会调用driver.connect(url),得到一个数据库连接;driver是数据库驱动,即具体工厂,其抽象工厂为java.sql.Driver,数据库连接即具体产品,其抽象产品为java.sql.Connection
4、建造模式(Builder)
public class ConcreteBuilder extends AbstractBuilder{
private Product product = new Product();
public void buildPartA() {
product.setPartA("A");
}
public void buildPartB() {
product.setPartB("B");
}
public Product getProduct() {
return product;
}
}
理解:用一个类来构建另一个类的实例,提取构建过程,方便复用
JDK案例:StringBuilder和StringBuffer用来建造String
5、原型模式(Prototype)
理解:以自己为原型创建新对象,即克隆
JDK案例:java.lang.Object#clone()、继承java.lang.Cloneable的类
6、适配器模式(Adapter)
类适配器
public class Adapter extends A implements B {
// 本类要实现的b1方法,要用到A中现成的方法,就可以extends A,然后就可以调用a1
public void b1() {
a1();
}
}
对象适配器
public class Adapter implements B {
private A a;
public Adapter(A a){
this.a = a;
}
// 本类要实现的b1方法,要用到A中现成的方法,就可以包含一个A实例,然后就可以调用a1
public void b1() {
...
a.a1();
...
}
}
理解:通过继承A或包含A,来让B的子类能调用A中现成的方法
JDK案例:
1、java.io.InputStreamReader(InputStream),字符流InputStreamReader在构造时得到字节流InputStream的实例,InputStreamReader.read()中即可调用InputStream.read()
2、java.io.OutputStreamWriter(OutputStream)
7、桥接模式(Bridge)
public class Bridge{
private A a; //抽象类
public void setA(A a) {
this.a = a;
}
protected void operation(){
...
a.a1(); // a1()的行为取决于a具体是什么对象
...
}
}
使用:
Bridge bridge = new Bridge();
bridge.setA(new A1());
bridge.operation();
bridge.setA(new A2());
bridge.operation();
理解:通过接收不同的A对象,来让本类有不同的表现
与对象适配器的区别:对象适配器是想利用A,桥接器是想接收不同的A
JDK案例:java.util.logging.Handler.setFilter(Filter)
8、组合模式(部分整体模式,Composite)
public class File extends Node { //叶节点
public void display() { }
}
public class Folder extends Node { //枝节点
List nodeList = new ArrayList(); //枝节点包含节点列表
public void display() {
for(Node node:nodeList){
node.display();
}
}
}
理解:组合模式适用于树状结构,叶节点和枝节点继承相同的父类,枝节点包含一个节点列表,方便递归访问
JDK案例:java.awt包中Container和Button都继承自Component,同时Container里包含Component列表
9、装饰者模式(Decoration)
// 被装饰者
public class Chicken extends Food {
private String desc = "鸡肉";
public String getDesc() {
return desc;
}
}
// 装饰者
public class SteamedFood extends Food {
private Food food;
public SteamedFood(Food f){
this.food = f;
}
public String getDesc() {
return "蒸" + food.getDesc();
}
}
使用:
Food fod = new Chicken();
Food sf = new SteamedFood(food); //装饰
sf.getDesc();
理解:目的是对原对象进行一些改造,并且改造后得到的类型不变
JDK案例:
1、java.io.BufferedInputStream(InputStream),BufferedInputStream是InputStream的子类
2、java.io.BufferedOutputStream(OutputStream),BufferedOutputStream是OutputStream的子类
10、外观模式(门面模式,Facade)
class Faced
{
SubSystemOne one; // 子系统
SubSystemTwo two;
SubSystemThree three;
public Faced()
{
one = new SubSystemOne();
two = new SubSystemTwo();
three = new SubSystemThree();
}
public void MethodA()
{
one.MethdOne();
two.MethodTwo();
}
public void MethodB()
{
one.MethdOne();
three.MethdThree();
}
}
理解:门面类 组合 一些子系统对象,供外界统一访问
JDK案例:JDBC 封装了许多操作数据库的子系统
11、享元模式(Flyweight)
// 享原类
public class ConcreteFlyweight implements Flyweight {
private String state = null;
public ConcreteFlyweight(String state){
this.state = state;
}
public void operation() {
}
}
// 享元工厂类
public class FlyweightFactory {
// 享元Map
private Map flyweightMap= new HashMap();
public Flyweight factory(String state){
Flyweight fly = flyweightMap.get(state);
if(fly == null){
fly = new ConcreteFlyweight(state);
flyweightMap.put(state, fly);
}
return fly;
}
}
理解:享元工厂维护一个享元Map,外界想要享元对象时,享元工厂先检查是否已有该state的对象,否才创建新享元对象
JDK案例:
1、java.lang.Integer#valueOf(int),Integer、Byte、Short、Long缓存了-128到127的整数
2、java.lang.Character#valueOf(char),Character缓存了0到127的码点
12、代理模式(Proxy)
静态代理
// 服务类
public class BuyHouseImpl implements BuyHouse {
public void buyHosue() {
System.out.println("我要买房");
}
}
// 静态代理类
public class BuyHouseProxy implements BuyHouse {
private BuyHouse buyHouse;
public BuyHouseProxy(final BuyHouse buyHouse) {
this.buyHouse = buyHouse;
}
public void buyHosue() {
...
buyHouse.buyHosue();
...
}
}
使用:
BuyHouse buyHouse = new BuyHouseImpl();
BuyHouseProxy buyHouseProxy = new BuyHouseProxy(buyHouse);
buyHouseProxy.buyHosue();
理解:类似于装饰器模式
动态代理
// 动态处理器
public class DynamicProxyHandler implements InvocationHandler { // java.lang.reflect.InvocationHandler
private Object object;
public DynamicProxyHandler(final Object object) {
this.object = object;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
...
Object result = method.invoke(object, args);
...
return result;
}
}
使用:
BuyHouse buyHouse = new BuyHouseImpl();
// 动态处理器实例
DynamicProxyHandler dph = new DynamicProxyHandler(buyHouse);
// 生成代理实例
BuyHouse proxyBuyHouse = (BuyHouse) Proxy.newProxyInstance(BuyHouse.class.getClassLoader(), new
Class[]{BuyHouse.class}, dph); // 第二个参数为接口数组,等同于 BuyHouseImpl.getClass().getInterfaces()
proxyBuyHouse.buyHosue();
理解:静态代理中,静态代理类只能代理一个服务类的指定方法;动态代理中,一个动态处理器,可以代理任何服务类的,父接口中有定义的方法
JDK案例:java.lang.reflect.Proxy
CGLIB代理
CGLIB(Code Generation Library)是一个开源类库
// 动态处理器
public class CglibProxy implements MethodInterceptor { // net.sf.cglib.proxy.MethodInterceptor
private Object target;
public Object getInstance(final Object target) {
this.target = target;
Enhancer enhancer = new Enhancer(); // net.sf.cglib.proxy.Enhancer
enhancer.setSuperclass(this.target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
// net.sf.cglib.proxy.MethodProxy
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
...
Object result = methodProxy.invoke(object, args);
...
return result;
}
}
使用:
BuyHouse buyHouse = new BuyHouseImpl();
// 动态处理器实例
CglibProxy cp = new CglibProxy();
// 生成代理实例
BuyHouseImpl buyHouseCglibProxy = (BuyHouseImpl) cp.getInstance(buyHouse);
buyHouseCglibProxy.buyHosue();
理解: CGLib通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。CGLIB动态代理的性能更高,但创建代理对象所花费的时间多。所以,无需频繁创建代理对象,用CGLIB合适,反之JDK合适。JDK动态代理只能代理接口中有定义的方法,CGLib动态代理只能代理非final类的方法。
13、策略模式(Strategy)
public class Context {
private A a; // 抽象策略类
public Context(A a){
this.a= a; // 持有具体策略对象
}
public void operation(){
...
a.a1(); // 执行效果取决于,持有的具体策略对象
...
}
}
理解:通过接收不同的A对象,来让自己的方法有不同的效果
与桥接模式的区别:桥接模式是让a对象成为自己的一部分,策略模式只是要利用a的方法(算法)
JDK案例:
1、java.util.Comparator#compare()
2、javax.servlet.http.HttpServlet
3、javax.servlet.Filter#doFilter()
14、模板方法模式(Template Method)
// 抽象类作为模板
public abstract class Template{
final void cookProcess(){ // final使得不被复写
this.HeatOil();
this.pourVegetable();
this.fry();
}
void HeatOil(){ // 模板中实现部分方法
System.out.println("热油");
}
abstract void pourVegetable();
abstract void fry();
}
public class BaoCai extend Template{
public void pourVegetable(){
System.out.println(”下包菜“);
}
public void fry(){
System.out.println(”炒包菜“);
}
}
使用:
BaoCai baoCai = new BaoCai();
baoCai.cookProcess();
理解:抽象实现部分方法,以便复用;子类重写部分方法,得以定制
JDK案例:
1、java.util.AbstractList 抽象类实现了部分方法,其子类ArrayList重写了原抽象方法get()
2、java.io.InputStream 抽象类实现了部分方法,其子类FileInputStream重写了原抽象方法read()
15、观察者模式(发布订阅模式,Observer)
// 被观察者类
public class WechatServer implements Observerable {
private List list = = new ArrayList(); // 维护一个观察者列表
private String message;
public void registerObserver(Observer o) { // 注册观察者
list.add(o);
}
public void removeObserver(Observer o) { // 移除观察者
list.remove(o);
}
public void notifyObserver() { // 通知所有观察者
for(int i = 0; i < list.size(); i++) {
Observer oserver = list.get(i);
oserver.update(message);
}
}
public void setMessage(String s) {
this.message = s;
notifyObserver();
}
}
// 观察者类
public class User implements Observer {
public void update(String message) {
System.out.println(" 收到推送消息: " + message);
}
}
理解:被观察者维护一个观察者列表,可以通过调用观察者的方法,来向所有观察者传递信息
JDK案例:实现 标记接口 java.util.EventListener 的类,即被观察者
16、迭代器模式(Iterator)
理解:迭代器持有集合,并提供方法 hasNext()、next() 来顺序访问集合
JDK案例:实现 java.util.Iterator 的类
17、责任链模式(Chain Of Responsibility)
// 责任人(处理者)类
public class ConcreteHandler1 extends Handler {
protected Handler successor; // 下一个责任人
public Handler getSuccessor() {
return successor;
}
public void setSuccessor(Handler successor) {
this.successor = successor;
}
public void handleRequest() {
System.out.println("处理请求");
if(getSuccessor() != null)
{
// 交给责任链里的下一个责任人继续处理
getSuccessor().handleRequest();
}
}
}
使用:
Handler handler1 = new ConcreteHandler1();
Handler handler2 = new ConcreteHandler2();
handler1.setSuccessor(handler2);
handler1.handleRequest();
理解:一个责任人持有下一个责任人,自己处理完后就把请求传递给下一个责任人,责任人之间的解耦
JDK案例:
1、java.util.logging.Logger#log()
2、javax.servlet.Filter#doFilter()
18、命令模式(Command)
// 命令接收者类
public class Receiver
{
public void Do()
{
}
}
// 命令类
public class ConcreteCommand implements ICommand
{
private Receiver receiver = null;
public ConcreteCommand(Receiver receiver)
{
this.receiver = receiver;
}
public void Execute()
{
this.receiver.Do();
}
}
// 命令调用者类
public class Invoker
{
private ICommand command = null;
public void SetCommand(ICommand command)
{
this.command = command;
}
public void RunCommand()
{
command.Execute();
}
}
使用:
Receiver receiver = new Receiver();
Invoker invoker = new Invoker();
invoker.SetCommand(new ConcreteCommandA(receiver)); // 调用A命令
invoker.RunCommand();
invoker.SetCommand(new ConcreteCommandB(receiver)); // 调用B命令
invoker.RunCommand();
理解:命令调用者 持有 命令,命令 持有 命令接收者。解耦调用者和接收者,在不改变命令接收者的情况下,可以通过新增和改动命令,来增强功能。
与装饰器模式的区别:装饰器模式强调属性增强,命令模式强调方法增强
JDK案例:java.lang.Runnable 即命令类的父接口
19、备忘录模式(Memento,快照模式,Snapshot)
// 备忘录
public class Memento {
private int state;
public Memento(int state) {
this.state = state;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
}
// 备忘录管理者
public class Caretaker {
private Memento memento;
public Memento getMemento() {
return this.memento;
}
public void saveMemento(Memento memento) {
this.memento = memento;
}
}
// 备忘录发起者
public class Originator {
private int state = 0;
public Memento createMemento() {
return new Memento(state);
}
public void restoreMemento(Memento memento) {
this.state = memento.getState();
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
}
使用:
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();
originator.setState(3);
// 发起者产生快照(备忘录),并由管理者保存
caretaker.saveMemento(originator.creatMemento());
originator.setState(5);
// 发起者接收一个备忘录,并恢复回备忘录记录的状态
originator.restoreMemento(caretaker.retrieveMemento());
理解:发起者可以产生和接收一个备忘录,管理者可以保存一个备忘录,发起者和管理者独立存在
JDK案例:java.util.Date的getTime() 和 setTime()相当于生成快照 和 恢复到快照记录的状态
20、状态模式(State)
public class Door implements DoorInterface {
public final static int OPENING_STATE = 1;
public final static int CLOSING_STATE = 2;
private int state;
public void setState(int state) {
this.state = state;
}
public void close() {
// 根据 state 做相应处理
}
public void open() {
// 根据 state 做相应处理
}
}
使用:
DoorInterface door = new Door();
lift.setState(Door.OPENING_STATE);
lift.open();
lift.close();
理解:通过改变对象内部的状态,来控制其行为
JDK案例:java.util.Iterator的next()即改变其状态
21、访问者模式(Visitor)
// 元素类(被访问者)
class ConsumeBill implements Bill {
private double amount;
public ConsumeBill(double amount) {
this.amount = amount;
}
public void accept(AccountBookVisitor visitor) {
visitor.visit(this); // 元素 接受 访问者访问时,回调访问者,将自己作为参数传递给访问者
}
public double getAmount() {
return amount;
}
}
// 访问者类
class Boss implements AccountBookVisitor {
private double totalConsume;
// 访问者 的 访问方法 由 被访问者调用,参数即被访问者
public void visit(ConsumerBill consumerBill) {
totalConsumer = totalConsumer + consumerBill.getAmount(); // 将访问结果 保存到 成员变量里
}
public void getTotalConsumer() {
System.out.println(totalConsumer);
}
}
// 对象结构 类(元素管理器)
class AccountBook {
private List listBill = new ArrayList(); // 对象结构维护一个被访问者列表
public void add(Bill bill) {
listBill.add(bill);
}
public void accept(AccountBookVisitor viewer) {
for (Bill b : listBill) {
b.accept(viewer);
}
}
}
使用:
// 元素管理器 添加元素
AccountBook accountBook = new AccountBook();
accountBook.add(new ConsumeBill("消费", 3000));
accountBook.add(new ConsumeBill("消费", 4000));
// 访问者
Boss boss = new Boss();
// 元素管理器 接受 访问,即所有元素接受访问
accountBook.accept(boss);
// 访问完 即 得到结果
boss.getTotalConsumer();
理解:元素管理器维护一个元素集合,元素管理器接受访问者访问,即所有元素接受访问;元素接受访问,即回调访问的访问方法。使得,在不改变元素和元素管理器的情况下,可以通过新增或更改访问者,来实现业务逻辑,且访问者中不需要遍历元素。
JDK案例:javax.lang.model.element.ElementVisitor 和 Element 即一对访问者 和 被访问者
22、中介者模式(调停者模式,Mediator)
// 同事类A
public class ConcreteColleagueA extends Colleague{
protected Mediator mediator;
public void setMediator(Mediator mediator) {
this.mediator = mediator;
}
public void notifyColleagueB() {
mediator.notifyColleagueB();
}
public void operation() { }
}
// 同事类B
public class ConcreteColleagueB extends Colleague{
protected Mediator mediator;
public void setMediator(Mediator mediator) {
this.mediator = mediator;
}
public void notifyColleagueA() {
mediator.notifyColleagueA();
}
public void operation() { }
}
// 中介者
public class ConcreteMediator extends Mediator{
protected Colleague colleagueA;
protected Colleague colleagueB;
public Mediator(Colleague colleagueA, Colleague colleagueB) {
this.colleagueA = colleagueA;
this.colleagueB = colleagueB;
}
public void notifyColleagueA() { // 中介提供调用同事A的方法
colleagueA.operation();
}
public void notifyColleagueB() { // 中介提供调用同事B的方法
colleagueB.operation();
}
}
使用:
ConcreteColleagueA colleagueA = new ConcreteColleagueA();
ConcreteColleagueB colleagueB = new ConcreteColleagueB();
// 中介 持有 两个同事
Mediator mediator = new ConcreteMediator(colleagueA, colleagueB);
// 同事持有中介
colleagueA.setMediator(mediator);
colleagueB.setMediator(mediator);
// 同事可以通过调用中介 来间接调用另一个同事
colleagueA.notifyColleagueB();
colleagueB.notifyColleagueA();
理解:中介持有两个同事,并分别提供调用两个同事的方法;两个同事都持有中介,可以通过调用中介提供的方法,间接调用另一个同事
JDK案例:java.util.Timer 作为中介者,能持有多个同事,并调度
23、解释器模式(Interpreter)
理解:特定用于语法解释器,即,输入一个字符串,输出一个对象或数值
JDK案例:java.util.Pattern