(1)单一职责:每个类的极限原则是职责单一性;
(2)里氏替换原则:凡是父类出现的地方,子类一定适用。在编译器进行语法检查时可查出;
(3)依赖倒置原则:不同层次的功能对接时,必须是接口(抽象类)的对接,减少对具体代码的依赖。对于以后项目扩大,对代码的改动最少。(符合开闭原则);
(4)接口隔离原则:客户端不应该依赖其不需要的接口,类间的依赖关系应该建立在最小的接口上(如订单,门户网站只需要取订单,其它外部系统也只要读取订单,而admin需要对订单的增删改取,这样就可以建三个接口,各自用各自的,而不是用一个庞大臃肿的接口,甚至此接口还可能被污染,做些它本不应该做的事情。因此,使用多个接口比使用一个单一接口要好);
(5)迪米特法则:类应当尽量少的了解其它对象,又叫最少知识原则。具体体现在:只和朋友通信,不要和陌生人直接通信;
(6)开闭原则:是一种极限的理想状态,即只增加代码而不用修改代码。如设计一个Book类,有一个获取价格的方法,而在我们需求发生变化的时候如打折,我们可以写一个子类,重写获取价格的方法覆盖,在接口处直接拿就是。虽然开闭原则无法真正实现,但是在具体实现的过程中,我们只能不断向它靠拢。
(1)Visitor模式的使用:
简介:对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,使用访问者模式将这些封装到类中。
您在朋友家做客,您是访问者,朋友接受您的访问,您通过朋友的描述,然后对朋友的描述做出一个判断。
主要类:被访问的对象都有一个接受访问者访问的接口;访问者接口实现中写具体的对访问对象操作。
https://www.runoob.com/design-pattern/visitor-pattern.html
DOM4J对Visitor的支持,这样可以大大缩减代码量,并且清楚易懂。了解设计模式的人都知道,Visitor是GOF设计模式之一。其主要原理就是两种类互相保有对方的引用,并且一种作为Visitor去访问许多Visitable。我们来看DOM4J中的Visitor模式(快速文档中没有提供)
只需要自定一个类实现Visitor接口即可。
public class MyVisitor extends VisitorSupport {
public void visit(Element element){
System.out.println(element.getName());
}
public void visit(Attribute attr){
System.out.println(attr.getName());
}
}
// 调用:
root.accept(new MyVisitor())
Visitor接口提供多种Visit()的重载,根据XML不同的对象,将采用不同的方式来访问。上面是给出的Element和Attribute的简单实现,一般比较常用的就是这两个。VisitorSupport是DOM4J提供的默认适配器,Visitor接口的Default Adapter模式,这个模式给出了各种visit(*)的空实现,以便简化代码。
注意,这个Visitor是自动遍历所有子节点的。如果是root.accept(MyVisitor),将遍历子节点。我第一次用的时候,认为是需要自己遍历,便在递归中调用Visitor,结果可想而知。
(2)Decorator模式使用:
InputStream类,详情见问题3
(1)适配器模式。多个类不兼容,系统可以建一个适配器,通过适配器选择适合的类的方法来执行。
应用实例:在一次估算房产价格的流程中,有很多不同的测算方法,各个方法之间是相互独立的,那么可以为这些方法建立一个适配器接口,每个测算方法都有一个适配器类(都实现适配器接口),使用时,遍历这些适配器类找到合适的适配器,就能执行相应的方法了。优点在于新增方法时,不需要修改查找方法的代码,符合“开闭原则”。
https://juejin.im/post/5ba28986f265da0abc2b6084#heading-5
缺点: 过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构
(2)建造者模式。多个简单的对象一步一步构建成一个复杂的对象。
应用实例: 1、去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐"。 2、JAVA 中的 StringBuilder。Lombok中的@Builder注解。
实现建造者模式的代码为
public class User {
private Integer id;
private String name;
private String address;
private User() {
}
private User(User origin) {
this.id = origin.id;
this.name = origin.name;
this.address = origin.address;
}
public static User.Builder builder() {
return new User.Builder();
}
public static class Builder {
private User target;
public Builder() {
this.target = new User();
}
public Builder id(Integer id) {
target.id = id;
return this;
}
public Builder name(String name) {
target.name = name;
return this;
}
public Builder address(String address) {
target.address = address;
return this;
}
public User build() {
return new User(target);
}
}
}
如果项目中有使用lombok的话,可以直接使用@Builder注解来实现
import lombok.Builder;
import lombok.ToString;
@ToString
@Builder
public class UserExample {
private Integer id;
private String name;
private String address;
}
使用
UserExample userExample = UserExample.builder()
.id(1)
.name("aaa")
.address("bbb")
.build();
System.out.println(userExample);
(3)装饰者模式
用来修饰对象的。
应用实例:例如一碗豆浆,加不同的配料后价格不一样,不同配料形成了不同的套餐,比如加了火腿的豆浆。装饰者模式与建造者模式的区别在于,建造者用来组装复杂的对象,而装饰者用来修饰一个对象,给对象的属性做一些变更等。
主要涉及到的类有:
网上的例子一看就懂:https://blog.csdn.net/weixin_45393094/article/details/104859456
单例类再整个项目中只能有一个实例。单例类必须自己创建自己的唯一实例。单例类必须给所有其他对象提供这一实例。
(1)懒汉模式:类加载时不初始化
/**
* 非线程安全的懒汉模式
*/
public class Singleton {
private static Singleton singleton;
private Singleton(){}
public static Singleton getSingleton(){
if(singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
/**
* 线程安全的懒汉模式
*/
public class Singleton1 {
private static Singleton1 singleton;
private Singleton1(){}
public static synchronized Singleton1 getSingleton(){
if(singleton == null) {
singleton = new Singleton1();
}
return singleton;
}
}
/**
* synchronized修饰的同步方法比一般方法要慢很多,如果多次调用getInstance(),累积的性能损耗就比较大了。
* 因此就有了双重校验锁
* 更高效的线程安全的懒汉模式:双校验
*/
public class SingletonA2 {
private volatile static SingletonA2 instance;
private SingletonA2(){}
public static SingletonA2 getInstance() {
if (instance == null) {
synchronized (SingletonA2.class) {
if (instance == null) {
instance = new SingletonA2();
}
}
}
return instance;
}
}
/**
* 懒汉模式:内置静态类,线程安全
*/
public class SingletonC {
private static class SingletonHolder {
private static SingletonC instance = new SingletonC();
}
public static SingletonC getInstance() {
return SingletonHolder.instance;
}
}
(2)饿汉模式:饿汉模式在类加载的时候就对实例进行创建,是线程安全的,实例在整个程序周期都存在。
/**
* 饿汉模式,线程安全
*/
public class Singleton3 {
private static Singleton3 instance = new Singleton3();
private Singleton3() {
}
public static Singleton3 getInstance() {
return instance;
}
}
(3)枚举写法
单例是如何保证的:
/**
* 枚举写法
*/
public enum SingletonD {
INSTANCE;
public void doSomething() {
System.out.println("Hello singleton.");
}
}
https://blog.csdn.net/fly910905/article/details/79286680
(1)静态代理:代理类的源代码是由程序员编写的,在程序运行前,它的.class文件就已经存在了,这种代理类称为静态代理类
package proxy;
import java.util.Date;
public interface HelloService{
public String echo(String msg);
public Date getTime();
}
package proxy;
import java.util.Date;
public class HelloServiceImpl implements HelloService{
public String echo(String msg){
return "echo:"+msg;
}
public Date getTime(){
return new Date();
}
}
package proxy;
import java.util.Date;
public class HelloServiceProxy implements HelloService{
//表示被代理的HelloService 实例
private HelloService helloService;
public HelloServiceProxy(HelloService helloService){
this.helloService=helloService;
}
public void setHelloServiceProxy(HelloService helloService){
this.helloService=helloService;
}
public String echo(String msg){
//预处理
System.out.println("before calling echo()");
//调用被代理的HelloService 实例的echo()方法
String result=helloService.echo(msg);
//事后处理
System.out.println("after calling echo()");
return result;
}
public Date getTime(){
//预处理
System.out.println("before calling getTime()");
//调用被代理的HelloService 实例的getTime()方法
Date date=helloService.getTime();
//事后处理
System.out.println("after calling getTime()");
return date;
}
}
使用
public class Client1{
public static void main(String args[]){
HelloService helloService=new HelloServiceImpl();
HelloService helloServiceProxy=new HelloServiceProxy(helloService);
System.out.println(helloServiceProxy.echo("hello"));
}
}
运行Client1 类,打印结果如下:
before calling echo()
after calling echo()
echo:hello
(2)动态代理:动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。
package ProxyMode;
/*
* 抽象接口,对应类图中的Subject
*
*/
public interface Subject {
public void SujectShow();
}
package ProxyMode;
public class RealSubject implements Subject{
@Override
public void SujectShow() {
// TODO Auto-generated method stub
System.out.println("杀人是我指使的,我是幕后黑手!By---"+getClass());
}
}
package ProxyMode;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//建立InvocationHandler用来响应代理的任何调用
public class ProxyHandler implements InvocationHandler {
private Object proxied;
public ProxyHandler( Object proxied )
{
this.proxied = proxied;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("准备工作之前:");
//转调具体目标对象的方法
Object object= method.invoke( proxied, args);
System.out.println("工作已经做完了!");
return object;
}
}
package ProxyMode;
import java.lang.reflect.Proxy;
public class DynamicProxy {
public static void main( String args[] )
{
RealSubject real = new RealSubject();
Subject proxySubject = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(),
new Class[]{Subject.class},
new ProxyHandler(real));
proxySubject.SujectShow();;
}
}
测试结果
准备工作之前:
杀人是我指使的,我是幕后黑手!By---class ProxyMode.RealSubject
工作已经做完了!
http://blog.sina.com.cn/s/blog_72893c860100puzg.html
https://blog.csdn.net/baiye_xing/article/details/76427717
创作模式
抽象工厂模式 (通过创造性的方法来识别工厂本身,这又可以用于创建另一个抽象/接口类型)
建造者模式 (通过创建方法识别返回实例本身)
工厂模式 (可通过创建方法识别返回抽象/接口类型的实现)
原型模式 (通过创建方法识别,返回具有相同属性的其他实例)
单例模式(通过创造性方法识别,每次返回相同的实例(通常是自己))
结构模式
适配器模式 (可通过创建方法识别采用不同抽象/接口类型的实例,并返回自己/另一个抽象/接口类型的实现,其装饰/覆盖给定实例)
桥接模式 (可以通过创建方法识别采用不同抽象/接口类型的实例,并返回自己的使用给定实例的抽象/接口类型的实现)
一个虚构的例子将会new LinkedHashMap(LinkedHashSet< K>, List< V>)返回一个不可修改的链接映射,它不会克隆,而是使用它们。该java.util.Collections#newSetFromMap()和singletonXXX()方法却接近。
组合模式 (通过将具有相同抽象/接口类型的实例的行为方法识别为树结构)
装饰器模式 (通过创作方法识别采用相同抽象/接口类型的实例,添加额外的行为)
门面模式 (可通过内部使用不同独立抽象/接口类型实例的行为方法识别)
享元模式 (使用缓存来加速大量小对象的访问时间)
代理模式 (可通过创建方法识别,该方法返回给定的抽象/接口类型的实现,该类型依次代表/使用给定抽象/接口类型的不同实现)
行为模式
责任链模式 (通过行为方法识别(间接地)在队列中的相同抽象/接口类型的另一个实现中调用相同的方法)
命令模式 (可以通过抽象/接口类型中的行为方法识别,该方法在创建时由命令实现封装的不同抽象/接口类型的实现中调用方法)
解释器模式 (通过行为方法识别,返回结构不同的实例/给定实例/类型的类型;请注意,解析/格式化不是模式的一部分,确定模式以及如何应用它)
迭代器模式 (可通过行为方法识别,从队列中顺序返回不同类型的实例)
中介者模式 (通过采用不同的抽象/接口类型(通常使用命令模式)实例的行为方法来识别给定实例)
备忘录模式 (可以通过内部改变整个实例的状态的行为方法来识别)
观察者模式(或发布/订阅) (可以通过行为方法识别,根据自己的状态调用另一个抽象/接口类型的实例上的方法)
状态模式 (可以通过行为方法识别,根据可以从外部控制的实例的状态改变其行为)
策略 (可以通过抽象/接口类型中的行为方法识别,该方法在已经作为方法参数传递到策略实现中的不同抽象/接口类型的实现中调用方法)
模板方法 (可以由已经具有抽象类型定义的“默认”行为的行为方法识别)
访问者 (可以通过两种不同的抽象/接口类型识别,它们的方法定义为采用每个其他抽象/接口类型;实际上调用另一个抽象/接口类型的方法,另一个执行所需的策略)
Reactor 是一种应用在服务器端的开发模式(也有说法称 Reactor 是一种 IO 模式),目的是提高服务端程序的并发能力。
Reactor 模式的核心思想:减少等待。当遇到需要等待 IO 时,先释放资源,而在 IO 完成时,再通过事件驱动 (event driven) 的方式,继续接下来的处理。从整体上减少了资源的消耗。