设计模式
设计模式指软件开发过程中面临的一般问题的解决方案
为神马要学习设计模式?
编程时更加有条理性
培养编程思维
写出优雅的代码
增加代码的健壮性
提高工作效率
设计模式的分类
创建型模式有5种(在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。)
工厂模式(Factory Pattern)
抽象工厂模式(Abstract Factory Pattern)
单例模式(Singleton Pattern)
建造者模式(Builder Pattern)
原型模式(Prototype Pattern)
结构型模式有7种(类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式)
适配器模式(Adapter Pattern)
桥接模式(Bridge Pattern)
过滤器模式(Filter、Criteria Pattern)
组合模式(Composite Pattern)
装饰器模式(Decorator Pattern)
外观模式(Facade Pattern)
享元模式(Flyweight Pattern)
代理模式(Proxy Pattern)
行为有12种(适用于对象之间的通信。)
责任链模式(Chain of Responsibility Pattern)
命令模式(Command Pattern)
解释器模式(Interpreter Pattern)
迭代器模式(Iterator Pattern)
中介者模式(Mediator Pattern)
备忘录模式(Memento Pattern)
观察者模式(Observer Pattern)
状态模式(State Pattern)
空对象模式(Null Object Pattern)
策略模式(Strategy Pattern)
模板模式(Template Pattern)
访问者模式(Visitor Pattern)
J2EE 模式8种(适用于表现层|表示层。这些模式是由 Sun Java Center 鉴定的。)
MVC 模式(MVC Pattern)
业务代表模式(Business Delegate Pattern)
组合实体模式(Composite Entity Pattern)
数据访问对象模式(Data Access Object Pattern)
前端控制器模式(Front Controller Pattern)
拦截过滤器模式(Intercepting Filter Pattern)
服务定位器模式(Service Locator Pattern)
传输对象模式(Transfer Object Pattern)
设计模式之间的关系
我们着重介绍几种常用的模式
一、创建者模式:单列模式、工厂模式、原型模式
1.单列模式:
主要解决:一个全局使用的类频繁地创建与销毁。
何时使用:当您想控制实例数目,节省系统资源的时候。
如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。
关键代码:构造函数是私有的。
1、饿汉模式
立即加载
类加载时,会直接实例化单例对象,以后都返回该对象的引用
优点:没有加锁,执行效率高,线程安全的实例
缺点:类加载时,会直接实例化单例对象,不管我有没有使用到该单例对象,浪费内存
书写方式
public class Singleton {
//当构造方法私有化后,无法在当前类的外部去通过构造方法产生对象
// 创建本类的私有构造方法
private Singleton(){}
private static Singleton singleton = new Singleton();
// 提供一个静态方法 返回当前的对象
public static Singleton getInstance(){
return singleton;
}
}
2、懒汉模式:
延迟加载的单例模式
优点:不会占用内存,单线程情况下,是安全的
缺点:在多线程下多个线程可能同时执行singleton == null都为true,会创建多个实例在内存中
书写方式;
public static LazySingleton getInstance(){
if(singleton == null){
singleton = new LazySingleton();
}
return singleton;
}
3、双重检验
描述:这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
先判断实例是否为null,为null则使用synchronized锁住类 , 然后在同步块中,再一次判断实例是否为null,为null则初始化实例synchronized(需要锁的对象){}
书写方式
public static LazySingleton getInstance(){
if(singleton == null){// 判断对象是否被初始化
// 对象没有被初始化,则加上类锁
synchronized (LazySingleton.class){
// 同时只有一个线程能够到这里
if(singleton == null){
// 创建实例对象
singleton = new LazySingleton();
}
}
}
// 实例对象的引用
return singleton;
}
}
4、静态内部类(属于懒加载)
只有第一次调用getInstance方法时,才会加载实列
书写方式
public class InnerSingleton {
private InnerSingleton(){}
// 静态内部类
private static class Inner{
private static InnerSingleton instance = new InnerSingleton();
}
public static InnerSingleton getInstance(){
return Inner.instance;
}
}
5、枚举–创建的单列模式
书写方式:
public class EnumSingleton {
private EnumSingleton(){}
private static enum SinEnum{
// 自定义的枚举值
TEST;
private EnumSingleton es = new EnumSingleton();
}
public static EnumSingleton getInstance(){
SinEnum s = SinEnum.TEST;
return s.es;
}
}
2.工厂模式:
1、简单工厂:
实现步骤
1.定义了一个Product接口,含有方法play
2.提供实现Product接口的实现类
3.定义工厂类(Factory)
4.通过工厂类获取想要的对象,不用再关心具体的细节
2、工厂方法
1.定义抽象产品接口
2.根据抽象产品接口,提供实现产品接口的实现类
3.定义抽象工厂接口
4.为不同的具体产品,给出不同的具体工厂类
5.测试(把自己当作顾客)
3、抽象工厂
1.对生产的产品进行细化,然后抽象
2.具体产品类
3.根据不同的品牌给出工厂
3. 原型模式
根据一个已经存在的对象,创建一个和他一样的对象。-- 克隆
浅克隆-- 利用Object中clone()实现
1.让被克隆的类实现Cloneable接口
2.重写clone方法,方法访问修饰符public
3.对象.clone()的方式的到一个一样的对象
浅克隆指,被克隆的对象会产生一个新的,但是对象属性不会产生。
深度克隆
1.克隆对象所涉及的自定义类,需要实现序列化接口
2.在需要克隆的类中,添加一个方法,完成序列化反序列化即可。
二、结构模式:代理模式、装饰模式、适配器模式、
1.代理模式:(静态代理和动态代理)
主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
何时使用:想在访问一个类时做一些控制。
如何解决:增加中间层。
关键代码:实现与被代理类组合。
(1)静态代理
根据目标对象需要代理的行为,抽象出一个接口(包含了需要代理的行为),目标类和代理类都需要去实现该接口,然后将目标对象注入到代理类中,此时就可以在代理类中调用目标对象的行为,并为止附加非功能性逻辑
抽象接口
public interface IEat {
void eat();
}
//代理类
public class LifePeople implements IEat{
//目标对象
private IEat monitor;
//通过构造方法传入
public LifePeople(IEat monitor){
this.monitor = monitor;
}
@Override
public void eat() {
System.out.println("订餐");
monitor.eat();
System.out.println("开发票");
}
}
//目标类
public class Monitor implements IEat{
@Override
public void eat() {
System.out.println("请大家吃火锅");
}
}
//测试类
public class Test {
public static void main(String[] args) {
IEat live = new LifePeople(new Monitor());
live.eat();
}
}
(2)动态代理:(JDK代理)
第一步,实现接口InvocationHandler,然后重写invoke方法,在invoke方法中调用目标对的方法
第二步,提供一个自定义的方法,通过Proxy.newProxyInstance()得到代理对象
(3)动态代理:(cglib代理)
cglib代理第三方提供的代理方式 所以需要导入cglib的依赖
第一步,导入Cglib依赖(包)
cglib cglib 2.2.2第二步,实现接口MethodInterceptor,重写intercept方法,在其中完成目标对象的方法调用
public class CglibProxy implements MethodInterceptor {
//接口通过构造方法注入
private IEat target;
public CglibProxy(IEat target){
this.target = target;
}
public Object getProxytance(){
Enhancer en = new Enhancer();//创建工具类(Enhancer)对象
//设置目标父类对象
en.setSuperclass(target.getClass());
//设置回调函数
en.setCallback(this);
//创建代理对象
return en.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object returnInfo = methodProxy.invoke(o,objects);
return returnInfo;
}
}
第三步,提供自定义方法,通过工具类获取得到代理对象
书写测试类
装饰模式
1.根据对象抽象一个公共的接口
public interface Stuff {
/** 获取咖啡名称 */
String getName();
int getPrice();
}
2.根据接口给出不同的实现类(主料类(主料类产生的对象就是被装饰的对象) 和 配料类–装饰类)
主料类实现抽象接口
public class AStuff implements Stuff {
@Override
public String getName() {
return "雀巢";
}
@Override
public int getPrice() {
return 12;
}
}
public class BStuff implements Stuff {
@Override
public String getName() {
return "蓝山";
}
@Override
public int getPrice() {
return 28;
}
}
public class KStuff implements Stuff {
@Override
public String getName() {
return "卡布奇洛";
}
@Override
public int getPrice() {
return 30;
}
}
3.在配料类中注入被装饰的对象
配料类
public class Milk implements Stuff{
private Stuff coffee;
public Milk(Stuff coffee){
this.coffee = coffee;
}
@Override
public String getName() {
return coffee.getName() + "加奶" + " ";
}
@Override
public int getPrice() {
return coffee.getPrice() + 5;
}
}
public class suger implements Stuff{
private Stuff coffee;
public suger(Stuff coffee){
this.coffee = coffee;
}
@Override
public String getName() {
return coffee.getName() + "加糖"+ " ";
}
@Override
public int getPrice() {
return coffee.getPrice() + 3;
}
}
4.生产咖啡时,先生产主料(被修饰的对象),然后用配料不断去修饰主料
测试类
public class Test {
public static void main(String[] args) {
Stuff aStuff = new AStuff();
aStuff = new Milk(aStuff);
aStuff = new Milk(aStuff);
aStuff = new suger(aStuff);
aStuff = new suger(aStuff);
System.out.println(aStuff.getName() + "总价为" + aStuff.getPrice());
Stuff bStuff = new BStuff();
bStuff = new Milk(bStuff);
bStuff = new suger(bStuff);
System.out.println(bStuff.getName() + bStuff.getPrice());
}
}
输出结果为
雀巢加奶 加奶 加糖 加糖 总价为28
蓝山加奶 加糖 36
适配器模式
使得原本不兼容的两个接口(功能)可以兼容–搭建了两个接口间的桥梁
实现适配器的方案,继承或者依赖(推荐使用)
优点:可以让没有任何关联的类一起运行;
缺点:过多的使用适配器,会导致系统非常混乱,不容具体把控
行为模式:观察者模式
1.主题类(由它产生的对象 就是 被观察的对象) 继承 Observable的类
/**
* 商品观察者主题
*
* 此类需要让他成为主题类,则需要继承Observable类
*/
public class Product extends Observable {
private double price;
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
//通知观察者注意到主题的变化
this.setChanged();//设置变化点
this.notifyObservers(price);//通知观察者
}
}
2.观察者类(由它产生的对象 就是 观察者) 实现Observer接口
/**
* 观察者类 代理一号
* 需要实现接口observer
*/
public class ProductProxy implements Observer {
private double price;
/***
*
* @param o the observable object.//主题对象
* @param arg an argument passed to the {@code notifyObservers}
* method.//主题更新对象
*/
@Override
public void update(Observable o, Object arg) {
double factoryPrice = (double) arg;
this.price = factoryPrice * 1.5;
}
public double getPrice() {
return price;
}
}
重写update(当主题发生变化时,会调用方法)
3.首先产生主题对象(被观察对象),产生观察者对象,然后给主题对象设置观察者,最后通过更改主题的值,测试观察者是否有观测到主题值的改变
public class Test {
public static void main(String[] args) {
//产生主题对象 --- 被观察者
Product product = new Product();
ProductProxy p1 = new ProductProxy();
//添加观察者
product.addObserver(p1);
//调用主题对象方法
product.setPrice(3000);
System.out.println("出厂价格" + product.getPrice() + "代理商价格" + p1.getPrice());
}
}
好了我们的介绍就在这里结束了
总结:设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。