https://blog.csdn.net/jason0539/article/details/44956775
创建型模式
:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式
结构型模式
:适配器模式、装饰者模式、代理模式、外观模式、桥接模式、组合模式、享元模式
行为型模式
:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式
还有两类
:并发型模式和线程池模式。
https://www.runoob.com/design-pattern/design-pattern-tutorial.html
意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
主要代码:
//1. 调用方法,传入实体类
Fruit apple = FruitFactory.getFruit(Apple.class);
//2. 上面调用下面这个方法,会返回一个实体类对象
public static Fruit getFruit(Class<?> clazz) {
//这方法是动态加载class,先把类文件加载进来,再使用.newInstance()时创建了一个对象。
return (Fruit) Class.forName(clazz.getName()).newInstance();
}
意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
应用实例: 1、您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。
意图:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
使用场景: 1、QQ 换皮肤,一整套一起换。
意图:保证一个类仅有一个实例
,并提供一个访问它的全局访问点。(例如:一个班级只有一个班主任,全班同学都能找班主任聊天)
应用实例:
//静态内部类(推荐使用)
public class StaticClassSingleton {
//私有的构造方法,防止new
private StaticClassSingleton() {
}
public static StaticClassSingleton getInstance() {
return StaticClassSingletonHolder.instance;
}
/**
* 静态内部类
*/
private static class StaticClassSingletonHolder {
//第一次加载内部类的时候,实例化单例对象
private static final StaticClassSingleton instance = new StaticClassSingleton();
}
}
//懒汉式(双重检索)
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
意图:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。(也就是深克隆
)
应用实例:
//这是一个pojo类
public class Person implements Cloneable{
// 姓名
private String name;
// 年龄
private int age;
// 性别
private String sex;
//朋友
private List<String> friends;
public Person clone() {
try {
Person person = (Person)super.clone();
//list等对象类型不能通过clone()方法深拷贝,所以我们手动遍历进去
List<String> newfriends = new ArrayList<String>();
for(String friend : this.getFriends()) {
newfriends.add(friend);
}
person.setFriends(newfriends);
return person;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
意图:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。(点外卖选择套餐,就不用去想怎么搭配;目的:new一个pojo,直接调用一个方法为里面的n个变量赋值
)
应用实例:
实体类(需要被建造的类)
public class House {
// 地板
private String floor;
// 墙
private String wall;
// 屋顶
private String housetop;
}
建造者(以何种方式建造上面的实体类,当然,我们可以有多个建造者)
public class PingFangBuilder implements HouseBuilder {
House house = new House();
public void makeFloor() {
house.setFloor("平房-->地板");
}
public void makeHousetop() {
house.setHousetop("平房-->房顶");
}
public void makeWall() {
house.setWall("平房-->墙");
}
public House getHouse() {
return house;
}
}
监工角色(它来调用建造者里面的方法)
public void makeHouse(HouseBuilder builder) {
builder.makeFloor();
builder.makeWall();
builder.makeHousetop();
}
客户角色(它来使用)
public static void main(String[] args) {
//由工程队来修
HouseBuilder builder = new GongyuBuilder();
//设计者来做
HouseDirector director = new HouseDirector();
director.makeHouse(builder);
House house = builder.getHouse();
System.out.println(house.getFloor());
System.out.println(house.getWall());
System.out.println(house.getHousetop());
}
意图:动态
地给一个对象添加一些额外的职责
。就增加功能来说,装饰器模式相比生成子类更为灵活。
举例:每次英雄升级都会附加一个额外技能点学习技能,每次额外的技能就表示一个新的方法,所以我们需要再生成一个子类方法把这个新的方法放进去
意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换(诸葛亮的锦囊妙计,每一个锦囊就是一个策略
; )
注意
:如果一个系统的策略多于四个
,就需要考虑使用混合模式
,解决策略类膨胀的问题。
意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生
改变时,所有依赖
于它的对象
都得到通知
并被自动更新。(拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。 )
意图:运用共享技术有效地支持大量细粒度的对象。(new对象的时候调用下面这个工厂类中的方法。享元模式也就是浅克隆
)
主要代码
public class TeacherFactory {
private Map<String,Teacher> pool;
public TeacherFactory() {
pool = new HashMap<String,Teacher>();
}
//获取对象的方法(Teacher 是个pojo对象)
public Teacher getTeacher(String number) {
//1.判断map中是否存在同名数据
Teacher teacher = pool.get(number);
if(teacher == null) {
//2.不存在则new一个并赋值再存到map中去
teacher = new Teacher();
teacher.setNumber(number);
pool.put(number, teacher);
}
//3. 返回对象
return teacher;
}
}
*
https://www.cnblogs.com/cenyu/p/6289209.html
意图:为其他对象提供一种代理以控制对这个对象的访问。
实例:相当于中介,在为别人介绍房子的时候,我们可以加价之类的;房子就是被代理的对象,而加价之类的就是增强代码,而别人找不到房子会找中介就是帮助别人调用无法调用的方法
作用:开闭原则,在执行被代理方法前后,增加功能;帮助它人调用它无法调用的方法
被代理对象
与代理对象一定要
实现相同的接口或者是继承相同父类.代理对象
public class UserDaoProxy implements IUserDao{
//接收保存目标对象
private IUserDao target;
public UserDaoProxy(IUserDao target){
this.target=target;
}
public void save() {
System.out.println("开始事务...");
target.save();//执行目标对象的方法
System.out.println("提交事务...");
}
}
-------------------------
main方法
public static void main(String[] args) {
//目标对象
UserDao target = new UserDao();
//代理对象,把目标对象传给代理对象,建立代理关系
UserDaoProxy proxy = new UserDaoProxy(target);
//执行的是代理的方法
proxy.save();
}
被代理对象一定要实现接口
代理对象
public class ProxyFactory {
//维护一个目标对象
private Object target;
public ProxyFactory(Object target){
this.target=target;
}
//给目标对象生成代理对象
/*
static Object newProxyInstance(ClassLoader loader, Class>[] interfaces,InvocationHandler h )
ClassLoader loader,:指定当前目标对象使用类加载器,获取加载器的方法是固定的
Class>[] interfaces,:目标对象实现的接口的类型,使用泛型方式确认类型
InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入
*/
public Object getProxyInstance(){
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始事务2");
//执行目标对象方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事务2");
return returnValue;
}
}
);
}
}
---------------
main方法
public static void main(String[] args) {
// 目标对象
IUserDao target = new UserDao();
// 【输出: class cn.itcast.b_dynamic.UserDao】
System.out.println(target.getClass());
// 给目标对象,创建代理对象
IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
// 【输出: class com.sun.proxy.$Proxy0】
System.out.println(proxy.getClass());
// 执行方法 【代理对象】
proxy.save();
}
在目标对象没有实现接口的时候用
)Cglib子类代理工厂
public class ProxyFactory implements MethodInterceptor {
//维护目标对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
//给目标对象创建一个代理对象
public Object getProxyInstance(){
//1.工具类
Enhancer en = new Enhancer();
//2.设置父类
en.setSuperclass(target.getClass());
//3.设置回调函数
en.setCallback(this);
//4.创建子类(代理对象)
return en.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("开始事务...");
//执行目标对象的方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事务...");
return returnValue;
}
------------------------
main方法
@Test
public void test(){
//目标对象
UserDao target = new UserDao();
//代理对象
UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();
//执行代理对象的方法
proxy.save();
}
总结 :在Spring的AOP编程中:
如果加入容器的目标对象有实现接口
,用JDK代理
如果目标对象没有实现接口
,用Cglib代理
意图:为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
实际应用1:买电脑不用单独买cpu、内存条之类的东西,直接买别人组装好的机器,而我们也不知道内部结构;
实际应用2:开关机电脑,直接按开机键,就开机了,我们不必知道启动流程,也不需要一个一个启动电脑的各个部件
意图:将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
应用实例:树状图的结构就是组合模式的凸显
;我们可以用组合模式创建树状图类型的结构(如下图的模仿电脑存储文件)
意图:将抽象部分与实现部分分离
,使它们都可以独立的变化。
特点:比较复杂,低耦合,高内聚(JDBC连接原理就是桥接模式)
例:a.我们要造一辆车,把车当作一座桥(抽象类),车可以有宝马和奥迪等品牌,这些品牌就当作桥一头的实体类;
b.现在这些车,还差一个发动机,发动机也有不同型号,这些型号就当作桥另一头的实体类;
c.而现在我们可以通过车这个抽象类连接两端,因为已经连接了,所以你想要怎么组合就怎么组合。
主要代码:
这就是桥接模式中的桥
public abstract class Car {
//这是我们需要连接的继承结构的接口
private Engine engine;
getEngine() setEngine()...
//在每次调用这个类的时候都需要传入需要桥接的对象
public Car(Engine engine) {
this.engine = engine;
}
public abstract void installEngine();
}
意图:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作
。
例:美国电器 110V,中国 220V,就要有一个适配器将 110V 转化为 220V;在 LINUX 上运行 WINDOWS 程序。
注意
:如果太多需要适配器的地方,那就不使用适配器,而是直接对系统进行重构。
主要代码:
适配器类
public class SDAdapterTF implements SDCard {
private TFCard tfCard;
public SDAdapterTF(TFCard tfCard) {
this.tfCard = tfCard;
}
@Override
public String readSD() {
System.out.println("适配器读取tf卡");
return tfCard.readTF();
}
@Override
public int writeSD(String msg) {
System.out.println("适配器写tf卡");
return tfCard.writeTF(msg);
}
}
main方法
public static void main(String[] args) {
//1. 获取电脑对象
Computer computer = new ThinkpadComputer();
//2. 获取sd卡对象(注意这里实力话的具体是哪个对象)
SDCard sdCard = new SDAdapterTF(new TFCardImpl());
//3. 调用方法
System.out.println(computer.readSD(sdCard));
}
意图:给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。(用的较少)
例:。。。
意图:用一个中介对象
来封装
一系列的对象交互
,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
例:MVC 框架,其中C(控制器)就是 M(模型)和 V(视图)的中介者。
如图
:我们就是要使复杂的结构变生下面这样,都通过中介来连接各个类
主要代码
@Data
public class MediatorStructure extends Mediator{
//首先中介结构必须知道所有房主和租房者的信息
private HouseOwner houseOwner;
private Tenant tenant;
public void constact(String message, Person person) {
if(person == houseOwner){ //如果是房主,则租房者获得信息
tenant.getMessage(message);
}
else{ //反之则是房主获得信息
houseOwner.getMessage(message);
}
}
}
意图:避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推
应用实例:JS 中的事件冒泡;slf4j日志处理的分级
主要代码:
最主要的一个方法:
当我们当前new的这个对象不能满足当前的要求时,我们就使用next中的变量;如下(main)所示,我们这样set不同的对象进去,
就形成了一条链,一环套一环
private Support next;
public Support setNext(Support next) {
this.next = next;
return next;
}
main方法:(括号里面都是new的实例对象,这里省略了点)
Support alice = new NoSupport("Alice");
Support bob = new LimitSupport("Bob", 100);
alice.setNext(bob).setNext(charlie).setNext(diana).setNext(elmo).setNext(fred);
意图:提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。
应用实例:JAVA 中的 iterator
。
意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
举个例子
: 比如我们做菜可以分为三个步骤 (1)备料 (2)具体做菜 (3)盛菜端给客人享用,这三部就是算法的骨架 ;然而做不同菜需要的料,做的方法,以及如何盛装给客人享用都是不同的这个就是不同的实现细节。
意图:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
使用场景:1、后悔药。 2、打游戏时的存档。
3、键盘上的撤回
主要代码:
Person中的代码
//创建一个备份
public Memento createMemento() {
return new Memento(name,sex,age);
}
//恢复备份,还原
public void setMemento(Memento memento) {
this.name = memento.getName();
this.sex = memento.getSex();
this.age = memento.getAge();
}
意图:允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。
使用场景:1、打游戏的时候角色可有不同状态:满血、残血、死亡。2、饮水机的水,不同水位的时候就是不同的状态。
作用:状态模式和命令模式一样,也可以用于消除 if...else 等条件选择语句
与策略模式的区别:状态模式需要在子类实现与context相关的一个状态行为。
意图:将
一个请求封装成
一个对象
,从而使您可以用不同的请求对客户进行参数化
。命令模式也支持撤销操作。
使用场景:认为是命令的地方都可以使用命令模式,比如: 1、GUI 中每一个按钮都是一条命令。 2、模拟 CMD。3、按下键盘上的按钮
作用:状态模式和命令模式一样,也可以用于消除 if...else 等条件选择语句
意图:主要将数据结构
与数据操作分离
。
应用实例:1、医院拿药,药单就是一个被访问者
,划价员需要看药单上的药查询价格,护士需要根据药单上的药名拿药(他们连个就是访问者)。(如下所示)
注意
:访问者可以对功能进行统一,可以做报表、UI、拦截器与过滤器。
1、开闭原则(Open Close Principle)
开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。
2、里氏代换原则(Liskov Substitution Principle)
里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。—— From Baidu 百科
3、依赖倒转原则(Dependence Inversion Principle)
这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。
4、接口隔离原则(Interface Segregation Principle)
这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。
5、迪米特法则(最少知道原则)(Demeter Principle)
为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。
6、合成复用原则(Composite Reuse Principle)
原则是尽量使用合成/聚合的方式,而不是使用继承。