面向对象六大原则
指导思想
-
可维护性(Maintainability)
修改功能,需要改动地方越少,可维护性越好
-
可复用性(Reusability)
代码可以被重复使用,总结类库
-
可扩展性(Extensibility/Scalability)
添加功能无需修改原来代码
-
灵活性(Flexibility/Mobility/Adaptability)
代码接口可以灵活调用
原则
-
单一职责原则
一个类别太大,别太累,负责单一职责,高内聚地耦合
-
开闭原则
对扩展开放,对修改关闭,尽量不修改原来代码进行扩展,抽象多态是开闭原则的关键
-
里氏替换
所有使用父类的地方,必须能够透明的使用子类对象(子类可以完全替代父类)
-
依赖倒置原则
面向接口(抽象)编程,而不是依赖具体
-
接口抽离原则
每一个接口承担独立的角色,避免子类实现不需要的方法,提供接口暴露只需最小接口(只暴露用到的接口)
-
迪米特法则
对于一个对象,不要和联系不大的对象耦合,尽量只用自己的东西,低耦合
设计模式
1.单例模式(Singleton)
应用场景:只需要有一个实例存在
创建方法:(前提:将构造方法定义为private,然后提供获取实例的方法)
-
饿汉式,定义个static的实例直接初始化。推荐使用。
private static final SingletonObj INSTANCE = new SingletonObj();
线程安全,jvm保证,类加载时候就会实例化。
-
静态语句块初始化,和第一种一样
private static final SingletonObj INSTANCE; static { INSTANCE = new SingletonObj(); }
-
懒汉式,用的时候才初始化
private static final SingletonObj INSTANCE; public static SingletonObj getINSTANCE { //并发时第一次可能new多个实例 if(INSTANCE == null){ INSTANCE = new SingletonObj(); } return INSTANCE; }
-
针对3的并发问题,通过加synchronized加方法上控制并发
private static final SingletonObj INSTANCE; //方法上加锁导致并发效率降低 public static synchronized SingletonObj getINSTANCE { if(INSTANCE == null){ INSTANCE = new SingletonObj(); } return INSTANCE; }
-
针对第4种效率问题,创建对象时加锁,双判断,很多人觉得是最完美写法。
//此处要加volatile保持可见性 private static final volatile SingletonObj INSTANCE; public static synchronized SingletonObj getINSTANCE { if(INSTANCE == null){ synchronized(XXX.class){ //此处如果不加判断,也会导致可能产生多个实例 if(INSTANCE == null){ INSTANCE = new SingletonObj(); } } } return INSTANCE; }
-
静态内部类的方式,JVM保证线程安全,完美写法之一。
private static class SingletonObjHolder { private final static SingletonObj INSTANCE = new SingletonObj(); } public static SingletonObj getINSTANCE { //调用时候静态内部类才会加载 return SingletonObjHolder.INSTANCE; }
-
通过枚举类,最完美写法(缺点是用枚举有点奇怪),线程安全,还可以防止反序列化(反射创建,枚举类没有构造方法)。
public enum SingletonObj{ INSTANCE; public void method(){} }
2.策略模式(Strategy)
在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。在有多种算法相似的情况下,解决使用 if…else 所带来的复杂和难以维护。
应用场景:比如分享功能,目前有微信和QQ,后续可能还会增加微博等等,此时只要定义不同的策略即可。
//java的Comparetor就是策略模式,此处也用比较方法举例
//创建一个比较器接口
public interface CustomerComparetor<T>{
int compareTo(T o1,T o2);
}
//自定义定义比较器实现比较器接口并重写方法
//统一排序方法调用时传入对应的比较器
public class Utils<T>{
public void sort(T[] arr,CustomerComparetor<T> comparetor){
//遍历通过comparetor.compareTo方法进行排序
}
}
3.工厂模式(Factory)
定义:任何可以产生对象的方法或类,都可以称之为工厂,单例也是一种工厂
-
简单工厂,扩展性不好
public class SimpleFactory{ public Car createCar(){ return new Car(); } public Plane createPlane(){ return Plane(); } }
-
工厂方法模式,分别创建工厂,单独分开,方便单一工厂扩展
public class CarFactory{ public Car create(){ return new Car(); } } public class PlaneFactory{ public Plane create(){ return new Car(); } }
-
抽象工厂,可以生产一系列的对象,对于一族的产品方便扩展,不方便单一扩展
//此处也可以用接口,可以根据语义选择使用,比如可以实现多个就用接口,只能属于一个类就抽象类 //抽象工厂 public abstract class AbstractFactory{ abstract AbstractCar createCar(); abstract AbstractPlane createPlane(); } //抽象类 public abstract class AbstractCar{ } //抽象类 public abstract class AbstractPlane{ } //具体工厂 public class Factory{ AbstractCar createCar(){ return new RedCar(); } AbstractPlane createPlane(){ return new BluePlane(); } } //具体类 public class RedCar extends AbstractCar{ } //具体类 public class AbstractPlane extends AbstractCar { } //比如还可以建立一个工厂创建白色的Car和黄色的plane
4.门面模式(Facade)
原本客户端是直接通过调用各个子系统的,通过门面模式,创建一个外观类,使得客户端直接调用外观类,来间接去操作相关子系统。
5.调停者(Mediator)
当各个系统互相各种调度,这时候可以抽出一个专门的系统,各个系统只跟这个系统调用。其实外部就是门面模式,内部就是调停者,应用场景消息中间件mq。
6. 责任链(chain of responsibility)
为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。filter就是用的责任链模式。
//这里使用filter来模拟案例
public interface filter{
//返回boolean用来控制是否继续往下执行
boolean doFilter(Msg msg);
}
//定义消息对象
public class Msg{
private String msg;
//...get set
}
//定义两个Filter
public class Filter1 implements Filter{
public boolean doFilter(Msg msg){
msg.setMsg(msg.getMsg+" Filter1 ");
}
}
public class Filter2 implements Filter{
public boolean doFilter(Msg msg){
msg.setMsg(msg.getMsg+" Filter2 ");
}
}
//定义责任链
public ChainFilter implements Filter{
List<Filter> list = new ArrayList<>();
public chainFilter addFilter(Filter filter){
list.add(filter);
return this;
}
public boolean doFilter(Msg msg){
for(Filter filter : list){
if(!filter.doFilter(msg)) return false;
}
return true;
}
}
//测试demo
public static void main(String[] args()){
ChainFilter chain = new ChainFilter();
//将filter加入到责任链
chain.add(new Filter1()).add(new Filter2());
//也可以将一个责任链作为一个整体filter加入
ChainFilter chain2 = new ChainFilter();
chain.add(new Filter1()).add(new Filter2()).add(chain);
chain2.doFilter(new Msg());
}
spring中filterChain对request顺序和对response顺序是相反的,实现很巧妙,简化代码类似如下:
//实现filter后需要重写此方法,传入三个参数
public class TextFilter implements Filter {
@Override
public void doFilter(Request request, Response response, FilterChain filterChain) {
//对request进行操作,通过递归,会一层层的先对request进行操作
//...
filterChain.doFilter(request, response, filterChain);
//对response进行操作,递归执行完request操作后会逆向对response进行操作
//...
}
}
7.装饰器(Decorator)
允许向一个现有的对象添加新的功能,同时又不改变其结构。一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。此时,将具体功能职责划分,同时继承装饰者模式。
使用场景:将具体功能职责划分,同时继承装饰者模式。
//创建一个接口
public interface Shape{
void draw();
}
//创建实体类
public class Circle implements Shape {
public void draw() {
System.out.println("Shape: Circle");
}
}
//创建抽象装饰类
public abstract class ShapeDecorator implements Shape {
protected Shape decoratedShape;
public ShapeDecorator(Shape decoratedShape){
this.decoratedShape = decoratedShape;
}
public void draw(){
decoratedShape.draw();
}
}
//创建装饰类
public class RedShapeDecorator extends ShapeDecorator {
public RedShapeDecorator(Shape decoratedShape) {
super(decoratedShape);
}
@Override
public void draw() {
decoratedShape.draw();
setRedBorder(decoratedShape);
}
private void setRedBorder(Shape decoratedShape){
System.out.println("Border Color: Red");
}
}
//小程序demo
public class DecoratorPatternDemo {
public static void main(String[] args) {
//将对象传入,通过装饰器进行装饰
ShapeDecorator redCircle = new RedShapeDecorator(new Circle());
redCircle.draw();
}
}
8. 观察者模式(Observer)
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。
Observer,Listener,Hook,Callback 都是观察者模式。一般用作事件监听,消息队列。
观察者模式与责任链模式很类似,责任链模式是将请求传递下去,观察者模式是对观察者进行广播。
9.组合模式(Composite)
组合模式依据树形结构来组合对象,用来表示部分以及整体层次。
使用场景:树形菜单,文件、文件夹的管理
abstract class Node {
abstract public void method();
}
//叶子节点
class LeafNode entends Node {
String content;
public LeafNode(String content){this.content = content}
public void method(){System.out.println(content)}
}
//分支节点
class BranchNode entends Node {
//当前子节点,可能是分支节点也可能是叶子节点
List<Node> nodes = new ArrayList<>();
String content;
public BranchNode(String content){
this.content = content;
}
public void method(){
System.out.println(content);
}
public void addNode(Node node){
nodes.add(node);
}
}
//构建树
public static void main(String[] args){
//根节点
BranchNode root = new BranchNode("root");
//创建分支节点并添加到根节点
BranchNode branch1 = new BranchNode("branch1");
BranchNode branch2= new BranchNode("branch2");
root.addNode(branch1);
root.addNode(branch2);
//创建叶子节点加入到分支branch1
LeafNode leaf1 = new LeafNode("leaf1");
LeafNode leaf2 = new LeafNode("leaf2");
branch1.addNode(leaf1);
branch1.addNode(leaf2);
//遍历树
tree(root,0);
}
//递归遍历,depth为深度
public static void tree(Node b,int depth){
for(int i=0;i<depth;i++){
System.out.print("---");
}
b.method();
//如果是分支则遍历节点进行递归
if(b instanceof BranchNode){
for(Node node : ((BranchNode) b).nodes){
tree(node,depth+1);
}
}
}
10.享元模式(flyWeight)
主要用于减少创建对象的数量,以减少内存占用和提高性能。池化思想,将对象放到池里,需要时从池里拿。java的String就是享元模式。应用场景一般是线程池,连接池。
11. 代理模式(Proxy)
-
静态代理: 直接创建一个代理类与被代理类实现同一个接口,在代理类中对同样方法进行操作
-
动态代理: 自动实现代理类,继承Proxy类,与被代理类实现同一接口
-
jdk : 通过反射实现,继承Proxy类,与被代理类实现同一接口
//通过newProxyInstance生成代理类,指定classLoader,代理类实现的接口列表和对应的执行handler Proxy proxy = (Proxy) Proxy.newProxyInstance(classLoader, interfaces, handler);
-
cglib:需要添加引入jar,通过继承被代理类实现,不需要实现接口,所以类是final的无法使用(ASM可以,因为直接修改字节码文件)
//创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数 Enhancer enhancer = new Enhancer(); //设置目标类的字节码文件 enhancer.setSuperclass(Dog.class); //设置回调函数,相当于jdk动态代理的handler enhancer.setCallback(new MyMethodInterceptor()); //这里的creat方法就是正式创建代理类 Proxy proxy = (Dog)enhancer.create();
-
Instrument:在JDK5引入的,在class被加载时可以直接修改二进制文件
-
12.迭代器模式(Iterator)
用于容器的遍历
//定义一个迭代器接口,要求每个容器都实现这个接口,实现通用遍历
//对于泛型的E T随便定义,只是E通常代表element,T代表type
public interface Iterator<E> {
//是否有下一个
boolean hasNext();
//获取下一个
E next();
}
13.访问者模式(Visitor)
在结构不变的情况下动态改变对于内部元素的动作,通过这种方式,元素的执行算法可以随着访问者改变而改变。代码耦合度很高。使用场景:1、对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。 2、需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作”污染”这些对象的类,也不希望在增加新操作时修改这些类。java的ASM是通过visitor实现的。
//定义一个表示元素的接口
public interface ComputerPart{
void accept(ComputerPartVistor vistor);
}
//创建元素实体类
public class Keyboard implements ComputerPart{
public void accept(ComputerPartVisitor vistor){
vistor.visit(this);
}
}
public class Monitor implements ComputerPart{
public void accept(ComputerPartVisitor vistor){
vistor.visit(this);
}
}
//创建一个访问者接口
public interface ComputerPartVistor{
public void visit(Keyboard keyboard);
public void visit(Monitor monitor);
}
//创建实体访问者
public class ComputerPartDisplayVistor implements ComputerPartVistor{
public void visit(Keyboard keyboard){
System.out.println("Displaying Keyboard.");
}
public void visit(Monitor monitor){
System.out.println("Displaying monitor.");
}
}
//测试Demo,相当于执行的方法调用了传入对象的相关方法
public class VisitorPatternDemo {
public static void main(String[] args) {
Monitor monitor = new Monitor();
monitor.accept(new ComputerPartDisplayVisitor());
}
}
14.构建器模式(Builder)
分离复杂对象的构建和表示
//当一个对象很大的时候可以分别构建
public class Person{
private String name;
private String age;
private String sex;
//..省略
}
public class PersonBuild {
Person person = new Person();
public Person buildName(String name){
person.name = name;
//链式编程
return this;
}
public Person buildAge(String age){
person.age = age;
return this;
}
public Person buildSex(String sex){
person.sex = sex;
return this;
}
public Person build(){
return person;
}
}
public static void main(String[] args){
PersonBuild build = new PersonBuild();
//可以灵活地构建对象的部分
person person = build.buildName("zhangsan").buildAge("20").buildSex("男").build();
}
15.适配器模式(Adapter)
作为两个不兼容的接口之间的桥梁。可以联想电源适配器,转接头
将一个类的接口转换成另外一个希望的接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
public class MediaAdapter implements MediaPlayer {
//播放器接口,有两个实现vlc播放器和mp4播放器
AdvancedMediaPlayer advancedMusicPlayer;
//根据类型构建不同的播放器
public MediaAdapter(String audioType){
if(audioType.equalsIgnoreCase("vlc") ){
advancedMusicPlayer = new VlcPlayer();
} else if (audioType.equalsIgnoreCase("mp4")){
advancedMusicPlayer = new Mp4Player();
}
}
//根据类型调用不同播放器的方法
@Override
public void play(String audioType, String fileName) {
if(audioType.equalsIgnoreCase("vlc")){
advancedMusicPlayer.playVlc(fileName);
}else if(audioType.equalsIgnoreCase("mp4")){
advancedMusicPlayer.playMp4(fileName);
}
}
}
16.桥接模式(Bridge)
将抽象部分和实现部分分离,使他们可以独立的变化。
//创建桥接接口
public interface DrawAPI{
public void drawCircle();
}
//创建桥接实现类
public class RedCircle implements DrawAPI{
public void drawCircle(){
System.out.println("Red circle");
}
}
public class GreenCircle implements DrawAPI{
public void drawCircle(){
System.out.println("Green circle");
}
}
//使用接口创建抽象类Shape
public abstract class Shape{
//抽象类中注入接口的实现类,可以直接调用对应实现类的方法
DrawAPI drawAPI;
Shape(DrawAPI drawAPI){
this.drawAPI = drawAPI;
}
public abstract void draw();
}
//创建实现类
public class Circle extends Shape {
public Circle(DrawAPI drawAPI){
super(drawAPI)
}
public void draw(){
drawAPI.drawCircle();
}
}
17.命令模式(Command)
封装命令
public abstract class Command {
//执行方法
public abstract void exec();
//回退方法
public abstract void undo();
}
//增加方法
public class AddCommand {
public void exec(){
System.out.println("加1");
}
public void undo(){
System.out.println("减1");
}
}
//可以结合组合模式实现宏命令(一系列串起来的命令)
//可以结合责任链模式做多次的undo
//可以结合记忆模式实现事务回滚
18.原型模式(Prototype)
jdk自带,实现cloneable接口,一般重写clone方法,jdk自带的clone方法克隆对象的引用类型还是指向的同一个地址(浅拷贝),重写clone方法使引用也克隆叫做深拷贝,属性也可以实现clone方法执行clone。
String类型不需要深拷贝,修改String是指向常量池中新的对象。但是如果用StringBuffer创建的就需要深拷贝。
19.备忘录模式(Memento)
备忘录模式提供的基本功能是:保存对象状态信息(快照)、撤销、重做和历史记录。
备忘录模式一般会提供两种接口:宽接口和窄接口。通过宽接口可以获取整个对象状态,会暴露备忘录对象的内部信息。通过窄接口,只能访问有限的,开发者限制了的信息,可以有效的防止信息泄露。
//一般需要存盘,要实现序列化
public class Memento implements Serializable {
private int money;
private int level;
//窄接口,访问部分信息
public int getMenoy(){
return menoy;
}
//窄接口,访问部分信息
public int getLevel(){
return level;
}
//宽接口,本包之内皆可访问
Memento(int menoy,int level){
//记录金钱和等级,可通过get获取值
this.menoy = menoy;
this.level = level;
}
}
20.模板方法(TemplateMethod)
模板方法就是钩子函数,回调函数
public class Main{
public static void main(String[] args){
F f = new C();
f.m();
}
}
abstract class F {
public void m(){
op1();
op2();
}
abstract void op1();
abstract void op2();
}
class C extends F {
void op1(){
System.out.println("1");
}
void op2(){
System.out.println("2");
}
}
21.状态模式(State)
一个类的动作根据状态不动有不同的类型反应
public abstract class MMState {
abstract void smile();
abstract void cry();
}
public class HappyState extends MMState {
void smile(){
System.out.println("happy smile");
}
void cry(){
System.out.println("happy cry");
}
}
public class SadState extends MMState {
void smile(){
System.out.println("sad smile");
}
void cry(){
System.out.println("sad cry");
}
}
public class MM{
String name;
//根据不同的状态调用不同实现
MMState state;
public void smile(){
state.smile();
}
}
22.解释器(Intepreter)
动态脚本解析,比如写一个正则表达式解析器,python解析器,解析一门语言。