精华内容
下载资源
问答
  • 主要介绍七大原则和23种设计模式 原则:1、单一原则2、开闭原则3、里氏替换原则4、接口隔离原则5、依赖倒置原则6、迪米特法则7、合成复用原则 设计模式:1、单例模式2、工厂模式3、抽象工厂模式4、原型模式5、建造者...

    一、前言

    Java设计模式在平时的开发中起到了至关重要的作用。设计模式的主要目的是:
    1、降低耦合度,使得类的修改不至于“牵一发而动全身”。
    2、提高可扩展性,新增功能对原有的代码没什么影响
    3、提高可复用性,降低过多的使用类时,导致类爆炸的情况
    4、提高灵活性,代码能够通过接口灵活调用
    5、提高可维护性,修改的地方越少

    二、七大原则

    1、单一原则:一个类只负责一个职责
    2、开闭原则:对修改关闭,对扩展开放
    3、里氏替换原则:不要破坏继承关系
    4、接口隔离原则:暴露最小接口,避免接口过于臃肿
    5、依赖倒置原则:面向抽象编程
    6、迪米特 法则:尽量不跟陌生人讲话
    7、合成复用原则:多使用组合、聚合、少用继承

    1、单一原则

    一个方法 一个类只负责一个职责,各个职责的程序改动,不影响其它程序,实现 高内聚,低耦合

    优点:降低耦合,提高可维护性,对一个类进行单独处理并且不影响其他类

    2、开闭原则

    对修改关闭,对扩展开放,核心是:抽象化,多态使用
    优点:提高可扩展性,提高可维护性,提高可复用性

    3、里氏替换原则

    所有使用父类的地方,必须能够透明的使用子类对象

    例如:父类Animal;子类Dog继承父类Animal,子类Cat继承父类Animal。

    Animal animal=new Dog();
    //同样可以透明的使用
    Animal animal=new Cat();
    

    当子类重写父类的方法时,则不适合使用里氏替换原则。

    4、接口隔离原则

    每一个接口承担独立的角色,只干自己该干的事情。
    只暴露最小接口,实现类不需要用到实现的方法不要放在接口。

    5、依赖倒置原则

    指的是面向抽象编程,对抽象类或接口进行依赖,比较灵活。
    比如:有抽象类Animal,子类Dog继承父类Animal,子类Cat继承父类Animal。在依赖的时候用抽象类,在使用的时候指定子类

    //抽象类和子类
    abstract class  Animal{
    	abstract void hello();
    }
    class Dog extends Animal{
    	@Override
    	void hello() {
    		System.out.println("hello ,I am dog");
    	}
    }
    class Cat extends Animal{
    	@Override
    	void hello() {
    		System.out.println("hello ,I am cat");	
    	}
    }	
    
    //依赖到抽象类
    class Client{
    Animal a;
    	public Client(Animal a){
    		this.a=a;
    	}
    	public void hello(){
    		a.hello();
    	}
    }
    //测试类进行使用
    public class Test {
    
        public static void main(String args[]) {
       //在使用到Animal的使用,需要定义它的子类
       	Client c=new Client(new Dog());
        c.hello();//打印指定的方法
        }
    }
    

    6、迪米特法则

    尽量不要跟陌生人说话。
    不是陌生人的情况:

    • 当前对象本身(this)
    • 方法参数
    • 对象的成员对象
    • 对象所创建的对象

    目的:降低耦合,高内聚

    7、合成复用原则

    尽量使用组合、聚合的方式,少使用继承。
    原因:继承会导致耦合度变高。
    聚合方式的使用案例:

    class A{}	
    class Test{
    A a;
    	public void useA(A a){
    		this.a=a;
    	}
    }
    

    组合方式的使用案例:

    class A{}	
    class Test{
    	A a=new A();
    }
    

    组合和聚合的区别 : 组合方式在使用Test对象的时候会立即开辟A对象的空间,而聚合的方式是在使用到A对象的时候才会开辟A对象的空间。

    三、23种设计模式

    23种设计模式
    创建型模式:主要关注点是“怎样创建对象”

    1、单例模式(Singleton)

    特点:当需求是一个类只需要一个实例
    单例模式有:1、饿汉模式;2、懒汉模式;3、双重检查模式;4、枚举模式

    饿汉模式(推荐使用)

    特点: 当类加载时创建好对象,并且在外部不能通过new创建该对象,只能调用这个类的的方法进行调用。线程安全,在实际开发中用的最多
    案例:
    方式一

    /**
     * 饿汉方式一
     * @Author:小庄
     */
    public class Singleton {
        //private不允许外部调用new创建对象
        private Singleton(){}
        //使用静态关键字”static“保证只有一次
        private static Singleton instance=new Singleton();
        //外部可直接通过类进行调用静态方法
        public static Singleton getInstance(){
            return instance;
        }
    }
    /**
     * 饿汉方式二
     * @Author:小庄
     */
    class Singleton2 {
        private static Singleton2 instance;
        private Singleton2(){
            instance=new Singleton2();
        }
    /**
     * 饿汉方式二
     * @Author:小庄
     */
    class Singleton2 {
        private static Singleton2 instance;
        static {
            instance=new Singleton2();
        }
        private Singleton2(){}
        //外部可直接通过类进行调用静态方法
        public static Singleton2 getInstance(){
            return instance;
        }
    }
    

    懒汉模式(不推荐使用)

    特点: 等需要该类的时候再加载,会考虑线程安全问题
    1、不安全的懒汉模式

    /**
     * 线程不安全的懒汉模式
     * @Author:小庄
     */
    class Singleton{
        private static Singleton instance;
        private Singleton(){}
        public static Singleton getInstance(){
        	instance=new Singleton();
            return instance;
        }
    }
    class Singleton2{
        private static Singleton2 instance;
        private Singleton2(){}
        public static  Singleton2 getInstance(){
        	if(instance==null){
    	    	synchronized(Singleton2.class){
    	    		instance=new Singleton2();
    	    	}	
        	}
            return instance;
        }
    }
    

    以上代码线程不安全的原因:当几个线程同时访问的时候,就有可能创建多个对象实例。

    2、线程安全的懒汉模式

    /**
     * 线程安全的懒汉模式
     * @Author:小庄
     */
    class Singleton{
        private static Singleton instance;
        private Singleton(){}
        public static synchronized Singleton getInstance(){
        	if(instance==null){
        		instance=new Singleton();
        	}
            return instance;
        }
    }
    

    以上代码缺点:当getInstance()方法里面的逻辑代码很复杂时,所有的代码都被加锁,会大大的消耗性能

    双重检查懒汉模式(线程安全)

    /**
     * 线程安全的懒汉模式
     * @Author:小庄
     */
    class Singleton{
        private static Singleton instance;
        private Singleton(){}
        public static  Singleton getInstance(){
        	if(instance==null){
        		//进行加锁
        		synchronized(Singleton.class){
        		//再进行一次判断类是否为空
    				if(instance==null){
    					instance=new Singleton();
    				}
    			}
        	}
            return instance;
        }
    }
    

    静态内部类实现单例

    /**
     *内部类实现单例
     * @Author:小庄
     */
    class Singleton{
        //设置为private
        private Singleton(){}
        //内部类的特性,外部类不能直接访问
        private static class GetSingleton{
           private static final Singleton instance=new Singleton();
        }
        public Singleton getInstance(){
            return GetSingleton.instance;
        }
    }
    

    运用枚举实现单例(推荐使用)

    enum Singleton2{
        ONE,TWO,THREE;//一个属性代表一个实例,这里有三个实例
    }
    

    小结

    推荐使用饿汉模式和枚举模式实现单例的原因是线程安全,而且简单明了
    不推荐使用懒汉模式的原因是:1、部分的懒汉模式不安全 ;2、过于繁琐 ;3、有可能会对性能造成额外消耗

    2、工厂模式(Factoy)

    特点:对类的创建用一个工厂类进行管理。

    工厂模式分为:简单工厂,工厂方法和抽象工厂,这节内容主要讲简单工厂和工厂方法。

    简单工厂

    由一个工厂对象决定创建出哪一种产品类的实例
    代替构造函数创建对象,方法名比构造函数清晰。
    简单工厂的类图:

    简单工厂
    具体代码实现:

    public class SimpleFactory{
    	public createProduct(int i){
    		switch (i){
                case 0: new Product1();break;
                case 1: new Product2();break;
                case 2: new Product3();break;
                default: System.out.println("没有该产品");
            }
    	}
    	//具体的使用者
    	public static void main(String[] args){
    		SimpleFactory factory=new SimpleFactory();
    		//通过工厂进行创建类
    		factory.createProduct(1);
    	}
    }
    class Product1{}
    class Product2{}
    class Product3{}
    
    

    工厂方法

    工厂方法

    //抽象类
    abstract class FactoryMethod {
    		//生产方法
    		public abstract void production(String productName);
    }
    class Product1 extends FactoryMethod {
    	//实现抽象方法
    	@Override
    	public void production(String productName) {
    		System.out.println("Product1生产了"+productName+"产品");
    		
    	}
    }
    class Product2 extends FactoryMethod {
    	//实现抽象方法
    	@Override
    	public void production(String productName) {
    		System.out.println("Product2生产了"+productName+"产品");
    		
    	}
    }
    //使用者
    public class Client{
    	public static void main(String[] args) {
    		FactoryMethod factory=new Product1();
    		factory.production("x");
    	}
    }
    

    3、抽象工厂模式(AbstractFactory)

    创建一组有关联的对象实例

    角色:

    • AbstractProduct (抽象产品)
    • AbstractFactory (抽象工厂)
    • Client (委托者)

    抽象工厂
    具体实现如下,为了保证代码的阅读,这里不对类的具体操作进行展开

    //定义抽象工厂类
    abstract class AbstractFactory {
    	//定义抽象方法,返回值是抽象产品类对象
    	abstract AbstractClass1 createClass1();
    	abstract AbstractClass2 createClass2();
    	abstract AbstractClass3 createClass3();
    }
    //把抽象产品类全部定义完
    abstract class AbstractClass1 {}
    abstract class AbstractClass2 {}
    abstract class AbstractClass3 {}
    //定义具体产品类,分别继承对应的抽象产品类
    class Class1 extends AbstractClass1{}
    class Class2 extends AbstractClass2{}
    class Class3 extends AbstractClass3{}
    //定义具体工厂类,实现抽象方法,具体工厂类1
    class Factory1 extends AbstractFactory{
    	@Override
    	AbstractClass1 createClass1() {
    		// TODO Auto-generated method stub
    		return new Class1();
    	}
    	@Override
    	AbstractClass2 createClass2() {
    		// TODO Auto-generated method stub
    		return new Class2();
    	}
    	@Override
    	AbstractClass3 createClass3() {
    		// TODO Auto-generated method stub
    		return new Class3();
    	}
    }
    //具体工厂类2
    class Factory2 extends AbstractFactory{
    	@Override
    	AbstractClass1 createClass1() {
    		// TODO Auto-generated method stub
    		return new Class1();
    	}
    	@Override
    	AbstractClass2 createClass2() {
    		// TODO Auto-generated method stub
    		return new Class2();
    	}
    	@Override
    	AbstractClass3 createClass3() {
    		// TODO Auto-generated method stub
    		return new Class3();
    	}
    }
    

    分析

    从上面代码中,我们可以发现,抽象工厂处理的是产品一族,它们和工厂方法的区别在于:工厂方法有利于处理一个产品的部件扩展维度,而抽象工厂有利于扩展产品一族维度。

    4、原型模式(Prototype)

    特点:必须实现Cloneable接口,并且重写clone方法,否则会报错
    应用场景:一个对象属性特别多,同时指定很麻烦,实际工作中用的很少

    角色:

    • Prototype(原型)
    • ConcretePrototype(具体原型)
    • Client(使用者)

    浅克隆

    //原型接口继承Cloneable接口,或者抽象类实现Cloneable接口
    interface Prototype extends Cloneable{
    }
    //具体原型类实现原型接口,重写方法
    public class ConcretePrototype implements Prototype {
        @Override
        protected Object clone() throws CloneNotSupportedException {
           //通过克隆自身对象
            ConcretePrototype p=(ConcretePrototype)this.clone();
            //返回克隆后的值
            return p;
        }
    }
    

    通过clone实现深拷贝

    //原型接口继承Cloneable接口,或者抽象类实现Cloneable接口
    interface Prototype extends Cloneable{
    }
    //具体原型类实现原型接口,重写方法
    public class ConcretePrototype implements Prototype {
        //假设有一个对象属性,需要克隆这个对象属性
        public Book book;
        @Override
        protected Object clone() throws CloneNotSupportedException {
            //对基本属性进行克隆
            Object deep=null;
            deep=super.clone();
            //对引用类型的数据进行单独处理
            ConcretePrototype c=(ConcretePrototype )deep;
            c.book= (Book)book.clone();
            //返回克隆后的值
            return deep;
        }
    }
    

    缺点:繁琐
    通过序列化实现深拷贝

    public class Book implements Serializable{
      
        //浅克隆,使用默认的clone方法
        public Object deepClone() {
    
            ByteArrayInputStream bis=null;
            ByteArrayOutputStream bos=null;
            ObjectOutputStream oos=null;
            ObjectInputStream ois=null;
            try{
                bos=new ByteArrayOutputStream();
                oos=new ObjectOutputStream(bos);
                oos.writeObject(this);
                //反序列化
                bis=new ByteArrayInputStream(bos.toByteArray());
                ois=new ObjectInputStream(bis);
                 Book book=(Book)ois.readObject();
                return book;
            } catch (IOException | ClassNotFoundException e) {
                e.printStackTrace();
                return null;
            }finally {
                try {
                    bos.close();
                    oos.close();
                    bis.close();
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

    5、建造者模式(Builder)

    特点:处理复杂对象的创建,在使用时只需要用想要的属性时,不需要设置所有参数;这个建造者模式和模板方法特别像

    角色:

    • Builder(建造者)
    • ConcreteBuilder(具体建造者)
    • Director(监工)
    • Client(使用者)
    //建造者接口或抽象类
    interface Builder {
    	//具体步骤
    	Builder creteBuilder();
    	Builder creteBuilder2();
    	Builder creteBuilder3();
    }
    //具体建造类
    class ConcreteBuilder implements Builder{
    	//实现具体步骤,返回值为当前对象,便于链式编程
    	@Override
    	public Builder creteBuilder() {
    		System.out.println("建造第一层");
    		return this;
    	}
    	@Override
    	public Builder creteBuilder2() {
    		System.out.println("建造第二层");
    		return this;
    	}
    	@Override
    	public Builder creteBuilder3() {
    		System.out.println("建造第三层");
    		return this;
    	}
    }
    //监工
    class Director {
    	//将抽象类(接口)聚合,然后对抽象方法进行调用,会指向子类实现的方法
    	Builder builder;
    	public Director(Builder builder) {
    		this.builder=builder;
    	}
    	public void toBuilder() {
    		builder.creteBuilder().creteBuilder2().creteBuilder3();
    	}
    }
    //使用者
    public class Client {
    	public static void main(String[] args) {
    		//通过监工直接建造
    		Director d=new Director(new ConcreteBuilder());
    		d.toBuilder();
    	}
    }
    

    比较常用的使用方式

    //模拟建造者模式的具体使用
    public class Person {
    	//这里定义了很多的属性,但是有时候只需要部分的属性
    	int id;
    	String name;
    	int age;
    	double weight;
    	int score;
    	//将构造函数设置为私有属性,不让外部进行访问
    	private Person() {}
    	//定义内部类,通过内部类对类的成员变量进行操作,可以叫它为静态工厂
    	public static class PersonBuilder{
    		Person p=new Person();
    		//每次方法返回值不为void,而是直接返回当前的对象,有利于链式方程
    		public PersonBuilder basicInfo(int id,String name,int age) {
    			p.id=id;
    			p.name=name;
    			p.age=age;
    			return this;
    		}
    		public PersonBuilder weight(double weight) {
    			p.weight=weight;
    			return this;
    		}
    		public PersonBuilder score(int score) {
    			p.score=score;
    			return this;
    		}
    		//通过下面方法返回外部类对象
    		public Person build() {return p;}
    	}
    	//打印
    	public static void main(String[] args) {
    		//采用链式编程,直观
    		Person p=new Person.PersonBuilder().basicInfo(1, "张三", 18).weight(65.0).build();
    	}
    }
    

    6、适配器模式(Adapte)

    使不兼容的接口相融

    角色:

    • Target(对象)
    • Adaptee(被适配)
    • Adapter(适配)

    1、类适配器模式
    类适配器

    //目标接口
    interface Target {
    	public int open110V();
    }
    //被适配类
    class ClassAdaptee{
    	public int open220V(){
    		return 200;
    	}
    }
    //适配类
    public class ClassAdapte extends ClassAdapteeimplements Target{
    	@Override
    	public int open110V() {
    		int voltage=open220V();
    		return voltage/2;
    	}
    }
    

    2、对象适配器

    对象适配器
    和类适配器的区别,适配器和被适配者之间没有继承关系,通过聚合实现(合成复用)

    //目标类,可以用抽象类代替
    abstract Target {
    	public abstract int open110V();
    }
    //被适配类
    class ClassAdaptee{
    	public int open220V(){
    		return 200;
    	}
    }
    //适配类
    public class ClassAdapte extends Target{
    	public ClassAdaptee adaptee;
    	public ClassAdapte(ClassAdaptee e){
    		this.adaptee=e;
    	}
    	@Override
    	public int open110V() {
    		int voltage=e.open220V();
    		return voltage/2;
    	}
    }
    

    7、桥连模式(Bridge)了解

    特点:
    双维度扩展
    分离抽象和具体
    用聚合的方式连接抽象和具体

    类图
    在这里插入图片描述

    abstract class AbstractBridge {
    	//聚合实现者
    	Implementor imt;
    }
    //具体桥连者
    class ConcreteBride extends AbstractBridge{
    	//把实现者作为参数
    	public ConcreteBride(Implementor imt) {
    		this.imt=imt;
    	}
    }
    //抽象化实现者
    interface Implementor {
    }
    //具体实现者
    public class ConcreteImplementor implements Implementor{
    }
    

    8、装饰者模式(Decorator)

    特点:对类进行扩展时不修改原有的代码,为类添加新的功能,防止类继承带来的爆炸性增长

    类图:
    装饰者模式

    案例应用:
    装饰者模式

    //定义Drink抽象类,并设定好抽象方法
     abstract class Drink {
    	private String des;//描述
    	private float price=0.0f;//价格
    	public String getDes() {
    		return des+"价格:"+price;
    	}
    	public void setDes(String des) {
    		this.des = des;
    	}
    	public float getPrice() {
    		return price;
    	}
    	public void setPrice(float price) {
    		this.price = price;
    	}
    	//计算费用的抽象方法
    	protected abstract float cost();
    }
    
    //Coffee类继承Drink抽象类,因为Coffee只是一种饮料
    abstract class Coffee extends Drink{
    }
    //定义具体咖啡,并设置好属性
    class Cafe_Latte extends Coffee{
    
    	public Cafe_Latte() {
    		super.setDes("拿铁咖啡");
    		super.setPrice(18.80f);
    	}
    	@Override
    	protected float cost() {
    		//获取价格
    		return super.getPrice();
    	}
    }
    //咖啡的具体实现类
    class Instant_Coffee extends Coffee{
    	
    	public Instant_Coffee(int shuliang) {
    		super.setDes("速溶咖啡");
    		super.setPrice(14.00f);
    	}
    	@Override
    	protected float cost() {
    		return super.getPrice();
    	}
    }
    class Cafe_Latte extends Coffee{
    
    	public Cafe_Latte() {
    		super.setDes("拿铁咖啡");
    		super.setPrice(18.80f);
    	}
    	@Override
    	protected float cost() {
    		return super.getPrice();
    	}
    }
    //通过继承Drink方便进行装饰类
     class DeCorator extends Drink{
    	private Drink drink;
    	private String des;//描述
    	private float price=0.0f;//价格
    	private int shuliang=1;//数量
    	public DeCorator(Drink drink) {
    		this.drink=drink;
    	}
    	public DeCorator(Drink drink,int shuliang) {
    		this.drink=drink;
    		this.shuliang=shuliang;
    	}
    	public String getDes() {
    		return drink.getDes()+" "+" 加入"+shuliang+"份"+des+" "+des+"单价:"+price;
    	}
    	public void setDes(String des) {
    		this.des = des;
    	}
    	public float getPrice() {
    		return price;
    	}
    	public void setPrice(float price) {
    		this.price = price;
    	}
    	protected float cost() {
    		float material_price=this.price*this.shuliang;
    		return material_price+drink.cost();
    	}
    }
    //装饰者的具体实现类
    class Milk extends DeCorator{
    	public Milk(Drink drink) {
    		super(drink);
    		super.setDes("牛奶");
    		super.setPrice(2f);
    	}
    	public Milk(Drink drink, int shuliang) {
    		super(drink, shuliang);
    		super.setDes("牛奶");
    		super.setPrice(2f);
    	}
    }
    //装饰者的具体实现类
    class Sugar extends DeCorator{
    	public Sugar(Drink drink) {
    		super(drink);
    		super.setDes("糖");
    		super.setPrice(1f);
    	}
    	public Sugar(Drink drink, int shuliang) {
    		super(drink, shuliang);
    		super.setDes("糖");
    		super.setPrice(1f);
    	}
    }
    //使用者测试类
    public class Client {
    	public static void main(String[] args) {
    		Drink order=new Cafe_Latte();
    		System.out.println("费用:"+order.cost());
    		System.out.println(order.getDes());
    		order=new Milk(order);
    		System.out.println("加入一份牛奶的费用:"+order.cost());
    		System.out.println(order.getDes());
    	}
    }
    
    

    9、享元模式(Flyweight)

    特点:共享数据,重复利用对象

    应用场景:数据库连接池

    类图:
    享元模式
    角色:

    • 抽象化享元对象
    • 享元对象
    • 享元池

    下面是图书馆案例,图书馆作为享元池,书架作为抽象化享元对象,书籍作为享元对象。

    来看类图
    享元模式

    //抽象化享元对象
    abstract class Book {
    	HashMap<String,Boolean> start=new HashMap<>();
    	abstract void borrow(Lender lender);
    	abstract void setBookStart(String bookName,boolean bookStart);
    	abstract boolean getBookStart(String bookName);
    }
    //具体享元对象
    class MixBook extends Book{
    	private String bookName="";//书名
    	private boolean bookStart;
    	public MixBook(String bookName) {
    		this.bookName=bookName;
    	}
    	@Override
    	public void borrow(Lender lender) {
    		System.out.println(lender.getName()+"向图书馆借出一本"+bookName);
    	}
    	public String getBookName() {
    		return bookName;
    	}
    	public void setBookName(String bookName) {
    		this.bookName = bookName;
    	}
    	public boolean getBookStart(String bookName) {
    		if(start.get(bookName)) return true;
    		else return false;
    	}
    	public void setBookStart(String bookName,boolean bookStart) {
    		start.put(bookName, bookStart);
    	}
    }
    //外部状态
    class Lender {
    	private String name;
    	public Lender(String name) {
    		this.name = name;
    		
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public String getName() {
    		return name;
    	}
    }
    import java.util.HashMap;
    //享元池
    class Library {
    	//设置图书馆为单例,只能生成一个实例对象
    	private static Library library=new Library();
    	//防止外部通过new生成实例
    	private Library(){};
    	//书架
    	HashMap<String,Book> pool=new HashMap<>();
    	
    	public static Library getInstance() {
    		return library;
    	}
    	public Book toBorrow(String bookName,Lender lender) {
    		Book book=null;
    		if(pool.isEmpty()) {
    			System.out.println("图书馆目前没有书,请添加书籍");
    		}
    		else if(!pool.containsKey(bookName)) {
    			//如果没有,提示图书馆没有这本书,请联系图书管理员
    			System.out.println("图书馆没有"+bookName+",请联系图书管理员");
    			
    		}else {
    			book=pool.get(bookName);
    			if(!book.getBookStart(bookName)){
    				System.out.println("你好"+lender.getName()+":"+bookName+"已经被借走了");
    				book=null;
    			}
    			else {
    				book.start.put(bookName, false);
    				book.borrow(lender);
    			}
    		}
    		return book;
    	}
    	public void shelveBook(String bookName,Book book) {
    		book.setBookStart(bookName,true);
    		pool.put(bookName, book);
    	}
    	public int poolSize() {
    		return pool.size();
    	}
    }
    //使用享元池
    public class Student {
    	public static void main(String[] args) {
    		//通过单例模式进行创建图书馆享元池
    		Library library=Library.getInstance();
    		library.shelveBook("《Java编程思想》", new MixBook("《Java编程思想》"));
    		library.shelveBook("《Mysql必会知识》", new MixBook("《Mysql必会知识》"));
    		//设置借阅人
    		Lender lender=new Lender("小庄");
    		Book book1=library.toBorrow("《Java编程思想》",lender);
    		Book book2=library.toBorrow("《Java编程思想》",lender);
    		Book book3=library.toBorrow("《Mysql必会知识》",lender);
    		//查看享元池借出的书籍
    		System.out.println("图书馆借出书籍共:"+library.poolSize()+"个");
    	}
    }
    

    10、代理模式(Proxy)

    作用:在程序运行期间,在不修改源代码的情况下对方法进行功能增强

    优势:减少重复代码,提高开发效率,并且便于维护

    底层实现:在运行期间,通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。

    代理模式分为:静态代理,动态代理
    本篇内容的动态代理只对jdk代理和cglib代理介绍

    静态代理

    静态代理
    角色:

    • 代理类
    • 被代理类
    • 抽象化被代理类
    • 使用者
    //抽象化被代理类
    interface IBoard {
    	void draw();
    }
    //具体被代理类
    class Board implements IBoard{
    	@Override
    	public void draw() {
    		System.out.println("被代理类画画操作执行");
    	}
    }
    //静态代理类
    class StaticProxy implements IBoard{
    	//聚合抽象化被代理类
    	private IBoard board;
    	public StaticProxy(IBoard board) {
    		this.board=board;
    	}
    	//通过代理类执行被代理类的方法
    	@Override
    	public void draw() {
    		System.out.println("代理类执行自己操作");
    		System.out.print("============");
    		board.draw();//调用被代理类执行的方法
    		System.out.print("============");
    		System.out.println("代理类执行结束");
    	}
    }
    //使用者
    public class Client {
    	public static void main(String[] args) {
    		//通过静态代理类进行调用被代理类的方法
    		new StaticProxy(new Board()).draw();
    	}
    }
    

    动态代理模式之JDK代理

    JDK代理
    jdk代理主要通过反射的方式进行代理,JDK代理本身就继承了Proxy类,由于Java不支持多继承,所以不支持对类的代理,只支持对接口的代理。
    具体案例如下:

    //接口1
    interface IBoard {
    	void draw();
    }
    //接口2
    interface IComputer {
    	void open();
    	void complete();
    }
    //接口1的实现类
    class Board implements IBoard{
    	@Override
    	public void draw() {
    		System.out.println("被代理类画画操作执行");
    	}
    }
    //接口2的实现类
    class Computer implements IComputer{
    	@Override
    	public void open() {
    		System.out.println("电脑正在启动....");	
    	}
    	@Override
    	public void complete() {
    		System.out.println("电脑启动完成");
    	}
    }
    //代理类
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    class JDKProxy {
    	//设置被代理对象
    	private Object target;
    	//通过构造函数设置被代理对象
    	public JDKProxy(Object target) {
    	 this.target=target;
    	}
    	//被代理对象生成
    	public Object getProxyInstance() {
    		//返回一个JDK代理类对象
    		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("=================");
    				System.out.println("代理开始");
    				Object object=method.invoke(target, args);
    				System.out.println("被代理的方法名:"+method.getName());
    				if("open"==method.getName()) {
    					Method complete=target.getClass().getMethod("complete");
    					if(null!=complete){
    						System.out.println("complete方法被代理对象自动调用");
    						Thread.sleep(1000);
    						complete.invoke(target, args);
    					}
    				}
    				System.out.println("代理结束");
    				System.out.println("=================");
    				System.out.println();
    				return object;
    			}
    		});
    	}
    }
    //使用者
    public class Client {
    	public static void main(String[] args) {
    		//代理画板对象
    		JDKProxy board=new JDKProxy(new Board());
    		IBoard iBoard=(IBoard)board.getProxyInstance();
    		//调用画板的draw()方法
    		iBoard.draw();
    		//代理电脑对象
    		JDKProxy computer=new JDKProxy(new Computer());
    		IComputer iComputer=(IComputer) computer.getProxyInstance();
    		//这里只调用了open()方法,但是complete会被代理对象自动调用
    		iComputer.open();
    	}
    }
    

    动态代理之cglib代理

    Cgilb代理
    我们知道,使用Jdk代理的不足之处是不能对类进行代理,而Cglib代理刚好解决了这个问题。
    Cglib可以对无接口的类进行代理,需要实现MethodInterceptor接口

    具体案例:

    //没有实现接口的类1
    class Board{
    	public void draw() {
    		System.out.println("被代理类画画操作执行");
    	}
    }
    //没有实现接口的类2
    class Computer{
    	public void open() {
    		System.out.println("电脑正在启动....");
    	}
    	public void complete() {
    		System.out.println("电脑启动完成");	
    	}
    }
    //Cglib代理
    import java.lang.reflect.Method;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    //实现MethodInterceptor接口,重写intercept方法(实现代理功能)
    class CglibProxy implements MethodInterceptor{
    	//设置被代理类
    	public Object target;
    	//通过构造函数进行设置
    	public CglibProxy(Object target) {
    		this.target=target;
    	}
    	/**
    	*实现代理的关键:
    	*1.创建工具类Enhancer
    	*2.设置它的父类,会在虚拟机生成一个父类
    	*3.设置回调函数
    	*4.创建子类对象,作为代理对象
    	*/
    	public Object getProxyInstance() {
    		//创建工具类
    		Enhancer e=new Enhancer();
    		//设置父类
    		e.setSuperclass(target.getClass());
    		//设置回调函数
    		e.setCallback(this);
    		//创建子类对象,作为代理对象
    		return e.create();
    	}
    	@Override
    	public Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable {
    		System.out.println("===============");
    		//代理模式底层使用反射
    		System.out.println("Cglib代理模式开始");
    		//invoke激活,和jdk代理功能类似
    		Object object=method.invoke(target, args);
    		System.out.println("被代理的方法名:"+method.getName());
    		//在代理类实现自己的功能
    		if("open"==method.getName()) {
    			Method complete=target.getClass().getMethod("complete");
    			if(null!=complete){
    				System.out.println("complete方法被代理对象自动调用");
    				Thread.sleep(1000);
    				complete.invoke(target, args);
    			}
    		}
    		System.out.println("代理结束");
    		System.out.println("=================");
    		System.out.println();
    		return object;
    	}
    }
    

    另一种方式:不需要类去实现MethodInterceptor接口,是在回调函数的实现这个接口

    //没有实现接口的类1
    class Board{
    	public void draw() {
    		System.out.println("被代理类画画操作执行");
    	}
    }
    //没有实现接口的类2
    class Computer{
    	public void open() {
    		System.out.println("电脑正在启动....");
    	}
    	public void complete() {
    		System.out.println("电脑启动完成");	
    	}
    }
    //Cglib代理
    import java.lang.reflect.Method;
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    //实现MethodInterceptor接口,重写intercept方法(实现代理功能)
    class CglibProxy2{
    	//设置被代理类
    	public Object target;
    	//通过构造函数进行设置
    	public CglibProxy2(Object target) {
    		this.target=target;
    	}
    	/**
    	*实现代理的关键:
    	*1.创建工具类Enhancer
    	*2.设置它的父类,会在虚拟机生成一个父类
    	*3.设置回调函数
    	*4.创建子类对象,作为代理对象
    	*/
    	public Object getProxyInstance() {
    		//创建工具类
    		Enhancer e=new Enhancer();
    		//设置父类
    		e.setSuperclass(target.getClass());
    		//设置回调函数
    		e.setCallback(new MethodInterceptor() {
    
    			@Override
    			public Object intercept(Object arg0, Method method, Object[] args, MethodProxy proxy) throws Throwable {
    				System.out.println("===============");
    				//代理模式底层使用反射
    				System.out.println("Cglib代理模式开始");
    				//invoke激活,和jdk代理功能类似
    				Object object=method.invoke(target, args);
    				System.out.println("被代理的方法名:"+method.getName());
    				//在代理类实现自己的功能
    				if("open"==method.getName()) {
    					Method complete=target.getClass().getMethod("complete");
    					if(null!=complete){
    						System.out.println("complete方法被代理对象自动调用");
    						Thread.sleep(1000);
    						complete.invoke(target, args);
    					}
    				}
    				System.out.println("代理结束");
    				System.out.println("=================");
    				System.out.println();
    				return object;
    			}
    		});
    		//创建子类对象,作为代理对象
    		return e.create();
    	}
    }
    

    11、组合模式(Composite)

    一般适用于树状结构的场景,比如文件结构,比如层级结构
    组合模式

    实现代码如下:

    //结点
    abstract class Node {
    	public abstract void printNode();
    }
    //导入相关的包
    import java.util.ArrayList;
    import java.util.List;
    //根结点/子结点
    class BranchNode extends Node{
    	//给结点起个名字
    	String nodeName;
    	//将它以下结点进行收集
    	public List<Node> nodes=new ArrayList<>();
    	public BranchNode(String nodeName) {
    		this.nodeName=nodeName;
    	}
    	public void addNode(Node node) {
    		nodes.add(node);
    	}
    	//打印结点
    	@Override
    	public void printNode() {
    		System.out.println(nodeName);
    	}
    }
    //叶子结点
    class LeafNode extends Node{
    	//给结点起个名字
    	String nodeName;
    	public LeafNode(String nodeName) {
    		this.nodeName=nodeName;
    	}
    	//打印叶子结点信息
    	@Override
    	public void printNode() {
    		System.out.println(nodeName);
    	}
    }
    //使用者
    public class Client {
    	public static void main(String[] args) {
    		BranchNode root=new BranchNode("根结点");
    		BranchNode node1=new BranchNode("结点1");
    		Node c1=new LeafNode("叶子结点1");
    		Node c2=new LeafNode("叶子结点2");
    		//添加结点
    		root.addNode(node1);
    		//添加叶子结点
    		node1.addNode(c1);
    		node1.addNode(c2);
    		//使用递归遍历	
    		tree(root,0);
    	}
    	public static void tree(Node node,int depth) {
    		for(int i=0;i<depth;i++) {
    			System.out.print("--");
    		}
    		node.printNode();
    		//判断是否是根结点
    		if(node instanceof BranchNode) {
    			//递归遍历结点
    			for(Node n:((BranchNode) node).nodes){
    				tree(n,depth+1);
    			}
    		}
    	}
    }
    

    生活中用到组合模式的例子:学校,学院(系)和专业
    组合模式的应用

    //结点
    abstract class Composite {
    	private String des;
    	private String name;
    	public Composite(String name,String des) {
    		this.name=name;
    		this.des=des;
    	}
    	protected void add(Composite composite) {
    	}
    	protected void remove(Composite composite) {}
    	protected abstract void print();
    	public String getDes() {
    		return des;
    	}
    	public void setDes(String des) {
    		this.des = des;
    	}
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    }
    //学院(系)
    import java.util.HashSet;
    
    class College extends Composite{
    	HashSet<Major> majors=new HashSet<>();
    	public College(String name,String des) {
    		super(name,des);
    	}
    	@Override
    	protected void add(Composite composite) {
    		majors.add((Major)composite);
    	}
    	@Override
    	protected void remove(Composite composite) {
    		majors.remove((Major)composite);
    	}
    	@Override
    	public String getName() {
    		return super.getName();
    	}
    	@Override
    	public String getDes() {
    		return super.getDes();
    	}
    	@Override
    	protected void print() {
    		System.out.println("学院:"+getName());
    		System.out.println();
    		for(Major major:majors) {
    		System.out.println("专业:"+major.getName()+"\t专业详情:"+major.getDes());
    		}
    		System.out.println("=====================");
    	}
    }
    //导入相关包
    
    import java.util.Set;
    //学校
    class University extends Composite{
    	//学校由多个学院(系)组成
    	Set<College> colleges=new HashSet<>();
    	
    	public University(String name,String des) {
    		super(name,des);
    	}
    	@Override
    	protected void add(Composite composite) {
    		colleges.add((College)composite);
    	}
    	@Override
    	protected void remove(Composite composite) {
    		colleges.remove((College)composite);
    	}
    	@Override
    	public String getName() {
    		return super.getName();
    	}
    	@Override
    	public String getDes() {
    		return super.getDes();
    	}
    	@Override
    	protected void print() {
    		System.out.println("========"+getName()+"========");
    		System.out.println("简介:"+getDes());
    		System.out.println();
    		for(College college:colleges) {
    		System.out.println("学院:"+college.getName()+"\t学院详情:"+college.getDes());
    		}
    		System.out.println("=====================");
    	}
    }
    //专业,对于叶子结点
    public class Major extends Composite{
    	public Major(String name,String des) {
    		super(name,des);
    	}
    	@Override
    	protected void print() {
    		System.out.println(super.getName()+super.getDes());
    	}
    }
    //使用者
    public class Client {
    public static void main(String[] args) {
    	University university=new University("清华大学", "中国顶尖学府");
    	College c=new College("计算机学院","培养软件+硬件高级人才");
    	university.add(c);
    	university.add(new College("金融学院", "培养金融行业高级人才"));
    	university.print();
    	c.add(new Major("计算机科学与技术","老牌的计算机专业"));
    	c.print();
    	}
    }
    

    12、外观模式(Facade)

    外观模式又称为门面模式,通常和责任链模式结合使用
    使用场景:解决多个类之间的复杂关系,只需要通过一个类就可以直接和其他类进行交互,具体使用:中间件

    通过案例进行讲解:
    外观模式

    //经纪人,门面
    class Agent {
    	private static Agent agent=new Agent();
    	//通过单例模式获取类对象
    	protected Star star=Star.getInstance();;
    	protected Fans fans=Fans.getInstance();
    	protected Company company=Company.getInstance();
    	private Agent() {}
    	//见面机会方法
    	protected void meeting() {
    		System.out.println("粉丝"+fans.getName()+"与"+star.getName()+"获得一次见面机会");
    	}
    	//签约方法
    	protected void contract() {
    		System.out.println("明星"+star.getName()+"与"+company.getName()+"进行了签约");
    	}
    	public static Agent getInstance() {
    		return agent;
    	}
    }
    //明星类
    class Star {
    	private static Star star=new Star();
    	protected volatile String name="";
    	private Star() {}
    	public String getName() {
    		return name;
    	}
    	public synchronized void setName(String name) {
    		this.name = name;
    	}
    	protected static Star getInstance() {
    		return star;
    	}
    }
    //粉丝
    class Fans {
    	private static Fans fans=new Fans();
    	private volatile String name="";
    	private Fans() {}
    	public String getName() {
    		return name;
    	}
    
    	public synchronized void setName(String name) {
    		this.name = name;
    	}
    	public static Fans getInstance() {
    		return fans;
    	}
    }
    //签约公司
    class Company {
    	private volatile String  name="";
    	private static Company company=new Company();
    	private Company() {}
    	public String getName() {
    		return name;
    	}
    	public synchronized void setName(String name) {
    		this.name = name;
    	}
    	public static Company getInstance() {
    		return  company;
    	}
    }
    //用户测试
    public class Client {
    	public static void main(String[] args) {
    		//直接跟经纪人打交道
    		Agent agent=Agent.getInstance();
    		agent.company.setName("公司x");
    		agent.fans.setName("张三");
    		agent.star.setName("吴签");
    		agent.meeting();
    		agent.contract();
    		System.out.println("============");
    		agent.fans.setName("李四");
    		agent.meeting();
    		agent.contract();
    	}
    }
    

    我们可以看到,经纪人就是一个门面,其他人都是通过经纪人来处理事情。粉丝想要见明星,首先要通过经纪人。再由经纪人和明星打交道。

    13、状态模式(State)

    如果一个类有很多个复杂状态的时候,可以把状态抽象出来,具体状态实现这个抽象类,

    角色:

    • State(状态)
    • ConcreteState(具体状态)
    • Context(状况)
    //设置抽象化状态类
    abstract class State {
    	public abstract void simle();
    	public abstract void cry();
    }
    //开心状态
    class HappyState extends State{
    	@Override
    	public void simle() {
    		System.out.println("开心的笑");
    	}
    	@Override
    	public void cry() {
    		System.out.println("开心的哭了");
    	}
    }
    //不开心状态
    class SadState extends State{
    	@Override
    	public void simle() {
    		System.out.println("沮丧的笑着");
    	}
    	@Override
    	public void cry() {
    		System.out.println("笑哭了");
    	}
    }
    //状况
    public class Context{
    	//聚合状态
    	private State state;
    	//设置状态
    	public Context(State state) {
    		this.state=state;
    	}
    	//根据状态判断
    	public void simle() {
    		state.simle();
    	}
    	//根据状态判断
    	public void cry() {
    		state.cry();
    	}
    }
    

    我们不难发现,抽象类的状态(抽象方法)是固定的,具体是怎样的状态,由子类实现。如果抽象类的状态(抽象方法)变化的很频繁,会对所有子类产生影响,那么就不建议使用这个模式

    14、责任链模式(Responsibility)

    是一种链式的结构的模式,通常使用在审批流,过滤器

    责任链模式

    //设置责任链的抽象类(接口)
    abstract class Approve {
    	private Approve approve;
    	//设置下一个审批人
    	public void setApprove(Approve approve) {
    		this.approve=approve;
    	}
    	//审批人的具体审批内容
    	public abstract void toApprove();
    	public abstract void chain(Approve approve);
    }
    //责任链的内容
    //审批人1
    class Approve1 extends Approve{
    	//写入审批人的审批内容
    	@Override
    	public void toApprove() {
    		System.out.println("我是审批人1,我审批完之后交给审批人2");
    		//设置它的下一个审批人
    		chain(new Approve2());
    	}
    	//设置它的下一个审批人
    	@Override
    	public void chain(Approve approve) {
    		approve.toApprove();
    	}
    }
    class Approve2 extends Approve {
    	@Override
    	public void toApprove() {
    		System.out.println("我是审批人2,我审批完之后交给审批人3");
    		chain(new Approve3());
    	}
    	@Override
    	public void chain(Approve approve) {
    		approve.toApprove();
    	}
    }
    class Approve3 extends Approve {
    	@Override
    	public void toApprove() {
    		System.out.println("我是审批人3,审批完成");
    		
    	}
    	@Override
    	public void chain(Approve approve) {
    		approve.toApprove();
    	}
    }
    //请求访问责任链
    public class Request {
    	public static void main(String[] args) {
    		Approve approve=new Approve1();
    		approve.toApprove();
    	}
    }
    

    换一种思路,我们把每一个子类存到容器中,然后对容器进行遍历,然后执行方法,也类似这种链式的执行流程。
    我们来通过代码来看看

    abstract class Approve {
    	//审批人的具体审批内容
    	public abstract void toApprove();
    }
    //审批人1
    class Approve1 extends Approve{
    	//写入审批人的审批内容
    	@Override
    	public void toApprove() {
    		System.out.println("我是审批人1,我审批完之后交给审批人2");
    	}
    }
    //审批人2
    class Approve2 extends Approve {
    	@Override
    	public void toApprove() {
    		System.out.println("我是审批人2,我审批完之后交给审批人3");
    	}
    }
    //审批人3
    class Approve3 extends Approve {
    	@Override
    	public void toApprove() {
    		System.out.println("我是审批人3,审批完成");
    	}
    }
    //导入集合
    import java.util.ArrayList;
    //责任链管理类
    class ApproveChain {
    	//聚合了内容
    	ArrayList<Approve> approves=new ArrayList<>();
    	public ApproveChain addChain(Approve approve) {
    		approves.add(approve);
    		return this;
    	}
    	//通过调用这个方法,遍历容器中的类执行方法
    	public void chain() {
    		for(Approve a:approves) {
    			a.toApprove();
    		}
    	}
    }
    //请求着使用
    public class Request {
    	public static void main(String[] args) {
    		//定义责任链管理类
    		ApproveChain ac=new ApproveChain();
    		//需要设置它的下一个
    		ac.addChain(new Approve1()).addChain(new Approve2()).addChain(new Approve3());
    		ac.chain();
    	}
    }
    

    上面代码很类似享元模式吧,哈哈。

    15、迭代器模式(Iterator)

    角色:

    • Iterator(迭代器)
    • ConcreteIterator(具体的迭代器)
    • Aggregate(集合)
    • CocreteAggreagete(具体集合)

    类图:
    迭代器模式
    Iterator(迭代器)

    public interface Iterator_ {
    	boolean hashNext();
    	Object next();
    }
    

    ConcreteIterator(具体的迭代器)

    public class ArrayListIterator implements Iterator_{
    	//单例模式创建类
    	private static ArrayListAggregate alat=new ArrayListAggregate();
    	@Override
    	public boolean hashNext() {
    		if(alat.currentIndex>=alat.size()) return true;
    		return false;
    	}
    	@Override
    	public Object next() {
    		Object o=alat.list.get(alat.currentIndex);
    		alat.currentIndex++;
    		return o;
    	}
    }
    

    Aggregate(集合接口)

    public interface Aggregate {
    	public abstract Iterator_ iterator();
    }
    

    CocreteAggreagete(具体集合)

    import java.util.ArrayList;
    
    public class ArrayListAggregate implements Aggregate{
    	ArrayList<Object> list=new ArrayList<>();
    	int index=0;
    	int currentIndex=0;
    	@Override
    	public Iterator_ iterator() {
    		// TODO Auto-generated method stub
    		return new ArrayListIterator();
    	}
    	public int size() {
    		
    		return index;
    	}
    	public void add(Object o) {
    		list.add(o);
    		index++;
    	}
    }
    

    16、观察者模式(Observer)重要

    我们开发中,使用到观察者模式很多,这个知识点要重点把握

    通知对象状态改变,允许一个对象向所有侦听对象广播自己的消息或事件

    角色:

    • Source:事件源对象
    • Observer:观察者
    • Event:事件

    观察者

    //事件,可定义为抽象化类(接口)
    class Event {
    	String loc;
    	public Event(String loc) {
    		this.loc=loc;
    	}
    }
    //抽象化观察者
    abstract class Observer {
    	public abstract void actionEvent(Event event);
    }
    //具体观察者1
    class Observer1 extends Observer{
    	@Override
    	public void actionEvent(Event event) {
    		//打印
    		System.out.println(event.loc);
    		System.out.println("观察者1做出了行为");
    		//执行相关的方法
    	}
    }
    //具体观察者2
    class Observer2 extends Observer{
    	@Override
    	public void actionEvent(Event event) {
    		System.out.println("============");
    		System.out.println("观察者2做出了行为");
    		//执行相关的操作
    	}
    }
    
    import java.util.ArrayList;
    import java.util.List;
    //由事件源发出事件,观察者进行处理事件
    public class Source {
    	private List<Observer> observers=new ArrayList<>();
    	{	
    		//注册观察者
    		observers.add(new Observer1());
    		observers.add(new Observer2());
    	}
    	//执行这个方法会触发所有观察者
    	public void wakeUp() {
    		for(Observer o:observers) {
    			o.actionEvent(new Event("bed"));
    		}
    	}
    }
    //测试
    class Test {
    	public static void main(String[] args) {
    		new Source().wakeUp();
    	}
    }
    

    17、策略模式(Strategy)

    策略模式
    角色:

    • 抽象化策略
    • 具体策略
    • 聚合策略类
    • 使用者

    封装的是不同的执行方式,比如对排序选择不同的排序算法(策略)
    代码如下

    //抽象化策略类(接口)
    interface Sorter{
    	public abstract void sort(Comparable[] data);
    }
    //具体策略,这里采用选择排序算法
    class SelectSorter implements Sorter{
    	@Override
    	public void sort(Comparable[] data) {
    		for(int i=0;i<data.length-1;i++) {
    			int min=i;
    			for(int j=i+1;j<data.length;j++) {
    				if(data[min].compareTo(data[j])>0) {
    					min=j;
    				}
    			}
    			swap(data,i,min);
    		}
    	}
    	public void swap(Comparable[] data,int i,int min) {
    		Comparable temp=data[i];
    		data[i]=data[min];
    		data[min]=temp;
    	}
    }
    //具体策略类,这里采用冒泡排序
    class BubbleSorter implements Sorter{
    	@Override
    	public void sort(Comparable[] data) {
    		//进行排序
    		for(int i=1;i<data.length;i++) {
    			for(int j=0;j<data.length-i;j++) {
    				if(data[j].compareTo(data[j+1])>0) {
    					Comparable temp=data[j+1];
    					data[j+1]=data[j];
    					data[j]=temp;
    				}
    			}
    		}
    	}
    }
    //聚合策略类,可以选择不同的策略
    class SortAndPrint{
    	Comparable[] data;
    	Sorter sorter;
    	public SortAndPrint(Comparable[] data,Sorter sorter) {
    		this.data=data;
    		this.sorter=sorter;
    	}
    	public void sort() {
    		System.out.println("====排序前====");
    		print();
    		sorter.sort(data);
    		System.out.println();
    		System.out.println("====排序后====");
    		print();
    	}
    	public void print() {
    		for(int i=0;i<data.length;i++) {
    			System.out.print(data[i]+",");
    		}
    		System.out.println(" ");
    	}
    }
    //测试
    public class Client {
    
    	public static void main(String[] args) {
    		//定义一个需要排序的数据
    		String[] data= {"Dumpty","Bowman","Carroll"};
    		SortAndPrint sap=new SortAndPrint(data, new BubbleSorter());
    		sap.sort();
    	}
    }
    

    18、模板方法模式(Temlapte Method)

    具体实现交给子类
    角色:

    • AbstractClass
    • concreteClass

    类图:
    模板方法模式

    具体使用:

    public abstract class AbstractDisplay {
    	//定义抽象方法,由子类去实现
    	public abstract void open();
    	public abstract void print();
    	public abstract void close();
    	//对这个方法设定为final类型。表示子类不能重写
    	public final void display() {
    		open();
    		print();
    		close();
    	}
    	//使用
    	public static void main(String[] args){
    		CharDisplay chars=new CharDisplay();
    		chars.display();
    	}
    }
    //子类继承
    class CharDisplay extends AbstractDisplay{
    	@Override
    	public void open() {
    		System.out.println("打开CharDisplay");	
    	}
    	@Override
    	public void print() {
    		System.out.println("在CharDisplay进行打印");
    	}
    	@Override
    	public void close() {
    		System.out.println("关闭CharDisplay");
    	}
    }
    //子类继承
    class StringDisplay extends AbstractDisplay{
    
    	@Override
    	public void open() {
    		System.out.println("打开StringDisplay");	
    	}
    
    	@Override
    	public void print() {
    		System.out.println("在StringDisplay进行打印");
    	}
    
    	@Override
    	public void close() {
    		System.out.println("关闭StringDisplay");
    	}
     	public void printLine() {
    		System.out.println("StringDisplay自定义的方法");
    	}
    }
    

    应用场景:

    1. 算法的整体步骤很固定,但其中个别部分易变,将易变部分交给子类实现。
    2. 多个子类存在公共行为,可以将其提取出并集中到一个公共父类以避免代码重复
    3. 需要控制子类的扩展时,模板方法只在特定点调用钩子函数操作,这样就只允许在这些点进行扩展。

    19、中介者模式(Mediator)

    中介这个角色在生活中的理解是:比如租房者找中介,再由中介跟相关的房东沟通,房东也是通过中介和租房者沟通。中介是中间的桥梁。
    在中介者模式中的理解和生活中的理解基本一致

    角色:

    • Mediator 抽象化中介者
    • ConcentrateMediator 具体中介者
    • College 抽象化同事类
    • ConcentrateCollege 具体同事类(租房者、房东)

    中介者模式(小庄)

    //抽象化中介者
    pabstract class Mediator {
    	//沟通的方法
    	public abstract void constact(String msg,Colleague c);
    }
    //具体中介类
    class ConcreteMediator extends Mediator{
    	//聚合同事类
    	private ConcreteColleague1 c1;
    	private ConcreteColleague2 c2;
    	//设置get和set方法
    	public ConcreteColleague1 getC1() {
    		return c1;
    	}
    	public void setC1(ConcreteColleague1 c1) {
    		this.c1 = c1;
    	}
    	public ConcreteColleague2 getC2() {
    		return c2;
    	}
    	public void setC2(ConcreteColleague2 c2) {
    		this.c2 = c2;
    	}
    	//具体的沟通方法,核心
    	@Override
    	public void constact(String msg, Colleague c) {
    		//判断是哪个同事,进行处理事件
    		if(c==c1) {
    			c2.getMessage(msg);
    		}else {
    			c1.getMessage(msg);
    		}
    	}
    }
    //同事类
    abstract class Colleague {
    	//给同事类命名
    	protected String name;
    	//设置中介
    	protected Mediator mediator;
    	public Colleague(String name,Mediator mediator) {
    		this.name=name;
    		this.mediator=mediator;
    	}
    	//沟通的方法
    	public abstract void constact(String msg);
    	//获取信息
    	public abstract void getMessage(String msg);
    }
    //具体的同事类,这个类表示租房
    class ConcreteColleague1 extends Colleague{
    	
    	//在构造函数让该类被创建的时候同时命名
    	public ConcreteColleague1(String name,Mediator mediator) {
    		super(name, mediator);
    	}
    	@Override
    	public void constact(String msg) {
    		mediator.constact(msg, this);
    	}
    	@Override
    	public void getMessage(String msg) {
    		System.out.println();
    		System.out.println("租房者姓名:"+name+"。收到的信息:"+msg);
    	}
    }
    //具体的同事类,这里表示房东
    class ConcreteColleague2 extends Colleague{
    
    	//在构造函数让该类被创建的时候同时命名
    	public ConcreteColleague2(String name,Mediator mediator) {
    		super(name,mediator);
    	}
    	@Override
    	public void constact(String msg) {
    		mediator.constact(msg, this);
    	}
    	@Override
    	public void getMessage(String msg) {
    		System.out.println("房东姓名:"+name+"。收到的信息:"+msg);
    	}
    }
    //测试类
    public class Client {
    	public static void main(String[] args) {
    		//创建中介者
    		ConcreteMediator cm=new ConcreteMediator();
    		//创建同事类
    		ConcreteColleague1 c1=new ConcreteColleague1("张三",cm);
    		ConcreteColleague2 c2=new ConcreteColleague2("李四",cm);
    		//房东需要知道具体的租房者和房东
    		cm.setC1(c1);
    		cm.setC2(c2);
    		c1.constact("我想租两房一厅的房子");
    		c2.constact("我这有,而且非常便宜");
    	}
    }
    

    20、备忘录模式(Memento)

    记录状态,便于回滚,经常和命令模式结合使用
    备忘录模式(小庄)
    角色:

    • Memento 备忘录,记录状态
    • Originator 创建者:创建备忘录
    • Caretaker 管理者:管理备忘录
    //备忘录,需要存的内容
    class Memento {
    	private String state;
    	public Memento(String state) {
    		this.state=state;
    	}
    	public String getState() {
    		return state;
    	}
    }
    //创建者,创建备忘录
    class Originator {
    	private String state;
    	public void setState(String state) {
    		this.state=state;
    		//打印状态
    		System.out.println("现在的状态为:"+state);
    	}
    	public String getState() {
    		return state;
    	}
    	//创建备忘录,并存储数据
    	public Memento saveMemento() {
    		return new Memento(state);
    	}
    	//获取备忘录的数据,恢复备忘录时的状态
    	public void getStateFromMemento(Memento memento) {
    		this.state=memento.getState();
    	}
    }
    //管理者,备忘录的管理
    import java.util.ArrayList;
    class Caretaker {
    	//聚合备忘录
    	private ArrayList<Memento> mementos=new ArrayList<>();
    	//添加备忘录
    	public void addMemento(Memento memento) {
    		mementos.add(memento);
    	}
    	public Memento getMemento(int index) {
    		return mementos.get(index);
    	}
    }
    //测试类
    public class Client {
    	public static void main(String[] args) {
    		//定义创建类,管理类
    		Originator o=new Originator();
    		Caretaker c=new Caretaker();
    		//设置当时的状态
    		o.setState("状态1:攻击力:1000");
    		c.addMemento(o.saveMemento());
    		o.setState("状态2:攻击力:800");
    		c.addMemento(o.saveMemento());
    		String state=c.getMemento(0).getState();
    		System.out.println("恢复后的状态为:"+state);
    	}
    }
    

    备忘录模式能够随时恢复到备忘录保存的状态,而命令模式需要多个撤销恢复上一个状态。备忘录模也会用在存盘功能,将会使用到序列化和反序化,对于序列化和反序列化的相关使用可以查看这里:

    序列化和反序列化的相关知识

    可以尝试实现这个功能,这里就不展开介绍了。

    21、命令模式(Command)

    需要回退(撤销,Undo)状态的时候通常会使用到命令模式
    多次撤销,需要结合责任链模式
    宏命令,需要树状结构的组合模式结合使用

    命令的调用者和命令的接收者进行了解耦

    角色:

    • invok 调用者
    • Command 命令
    • Receive 接收者

    命令者模式(小庄)
    下面代码是对单个命令的执行和撤销作用

    //抽象化命令接口
    interface Command {
    	void execute();
    	void undo();
    }
    //具体命令
    class Command1 implements Command{
    	private Receive receive; 
    	String str="https://blog.csdn.net/weixin_44715643?spm=1000.2115.3001.5343";
    	public Command1(Receive receive) {
    		this.receive=receive;
    	}
    	@Override
    	public void execute() {
    		System.out.println("接收者的内容:"+receive.msg);
    		receive.msg+=str;
    		System.out.println("接收者的内容发生了改变:"+receive.msg);
    	}
    	@Override
    	public void undo() {
    		//回退到原来的状态
    		receive.msg=receive.msg.substring(0, receive.msg.length()-str.length());
    		System.out.println("恢复回上一步的操作时的接收者的内容:"+receive.msg);
    	}
    }
    //接收者,对命令做出相应的动作
    class Receive {
    	 String msg=new String("hello everybody ");
    }
    //调用者,聚合了命令
    class Invok {
    	private Command command;
    	public Invok(Command command) {
    		this.command=command;
    	}
    	public void executeCommand() {
    		command.execute();
    	}
    	public void undoCommand() {
    		command.undo();
    	}
    }
    //使用命令者
    public class Client {
    	public static void main(String[] args) {
    		Invok invok=new Invok(new Command1(new Receive()));
    		//执行操作
    		invok.executeCommand();
    		//撤销操作
    		System.out.println("下面进行撤销操作");
    		invok.undoCommand();
    	}
    }
    

    多个命令的执行和撤销

    //抽象化命令接口
    abstract class Command {
    	abstract int getIndex();
    	abstract void execute();
    	abstract Command undo();
    }
    //命令1
    import java.util.ArrayList;
    class Command1 extends Command{
    	private ArrayList<String> alist=new ArrayList<>();
    	private int index=-1;
    	private Receive receive; 
    	//增加的内容
    	String str="xxx ";
    	public Command1(Receive receive) {
    		this.receive=receive;
    	}
    	@Override
    	public void execute() {	
    		System.out.println("接收者的内容:"+receive.msg);
    		receive.msg+=str;
    		System.out.println("接收者的内容发生了改变:"+receive.msg);
    		alist.add(receive.msg);
    		index++;
    	}
    	@Override
    	public Command1 undo() {
    		if(index>=0) {
    			if(index==0) {
    				receive.msg=receive.msg.substring(0,receive.chushizhi.length());
    				alist.remove(index);
    				index--;
    				System.out.println("初始值为:"+receive.msg);
    			}else {
    				//回退到上一步的状态
    				alist.remove(index);
    				//下标往前面移动一位
    				String s=alist.get(--index);
    				receive.msg=receive.msg.substring(0, s.length());
    				System.out.println("恢复回上一步的操作时的接收者的内容:"+receive.msg);
    			}
    		}else {
    			System.out.println("到底了,不能再撤销了");
    		}
    		return this;
    	}
    	//记录当前的状态
    	@Override
    	public int getIndex() {
    		// TODO Auto-generated method stub
    		return index;
    	}
    }
    //接收者,对命令做出相应的动作
    class Receive {
    	 String msg="hello everybody ";
    	//记录初始值
    	String chushizhi=msg;
    }
    //调用者,可以作为使用者Client
    class Invok {
    	private Command command;
    	public Invok(Command command) {
    		this.command=command;
    	}
    	public void executeCommand() {
    		command.execute();
    	}
    	public Invok undoCommand() {
    		command.undo();
    		return this;
    	}
    }
    //使用命令者
    public class Client {
    	public static void main(String[] args) {
    		Invok invok=new Invok(new Command1(new Receive()));
    		//执行操作
    		invok.executeCommand();
    		invok.executeCommand();
    		invok.executeCommand();
    		//撤销操作
    		System.out.println("下面进行撤销操作");
    		invok.undoCommand();
    		invok.undoCommand();
    		invok.undoCommand();
    		invok.undoCommand();
    	}
    }
    

    22、访问者模式(Visitor)

    数据结构和数据分离

    角色:

    • Visitor 访问者抽象类 - Element 具体元素
    • Struct 结构,由多个元素组成
    • 具体元素

    访问者模式

    //抽象化接口
    interface Visitor {
    	void visitCpu(Cpu cpu);
    	void visitMemory(Memory memory);
    	void visitBoard(Board board);
    }
    //具体访问者
    class Woman implements Visitor{
    	//全程七折半
    	double totalPrice=0.0;
    	@Override
    	public void visitCpu(Cpu cpu) {
    		//打折活动
    		totalPrice+=cpu.getPrice()*0.75;
    	}
    	@Override
    	public void visitMemory(Memory memory) {
    		//打折活动
    		totalPrice+=memory.getPrice()*0.75;
    	}
    
    	@Override
    	public void visitBoard(Board board) {
    		//打折活动
    		totalPrice+=board.getPrice()*0.75;
    	}
    	//获取价钱
    	public double getPrice() {
    		return totalPrice;
    	}
    }
    //具体访问者
    class Man implements Visitor{
    	//购买需要花费的总价钱,全场八折!
    	double totalPrice=0.0;
    	@Override
    	public void visitCpu(Cpu cpu) {
    		//打折活动
    		totalPrice+=cpu.getPrice()*0.8;
    	}
    	@Override
    	public void visitMemory(Memory memory) {
    		totalPrice+=memory.getPrice()*0.8;
    	}
    	@Override
    	public void visitBoard(Board board) {
    		totalPrice+=board.getPrice()*0.8;
    	}
    	//获取价钱
    	public double getPrice() {
    		return totalPrice;
    	}
    }
    //结构
    interface ComputerPart {
    	void accept(Visitor visitor);
    }
    class Board implements ComputerPart{
    	//接待指定的访问人,调用指定的方法
    	@Override
    	public void accept(Visitor visitor) {
    		visitor.visitBoard(this);
    	}
    	//设置Board的单价
    	public double getPrice() {
    		return 1000.00;
    	}
    }
    class Cpu implements ComputerPart{
    	//接待指定的访问人,调用指定的方法
    	@Override
    	public void accept(Visitor visitor) {
    		visitor.visitCpu(this);
    	}
    	//设置Cpu的单价
    	public double getPrice() {
    		return 800.0;
    	}
    }
    class Memory implements ComputerPart{
    	//接待指定的访问人,调用指定的方法
    	@Override
    	public void accept(Visitor visitor) {
    		visitor.visitMemory(this);
    	}
    	//设置Memory的单价
    	public double getPrice() {
    		return 400.00;
    	}
    }
    //结构类,根据不同的访问者设定标配的电脑配件
    public class Computer {
    	Cpu cpu=new Cpu();
    	Memory memory=new Memory();
    	Board board=new Board();
    	//给访问者设置标配
    	public void accept(Visitor visitor) {
    		this.cpu.accept(visitor);
    		this.memory.accept(visitor);
    		this.board.accept(visitor);
    	}
    }
    //测试类
    public class Test {
    	public static void main(String[] args) {
    		//定义相关类
    		Computer computer=new Computer();
    		Man man=new Man();
    		Woman woman=new Woman();
    		//通过访问者模式进行使用
    		//一个男人过来购买电脑
    		computer.accept(man);
    		System.out.println("男人过来买需要花费:"+man.getPrice());
    		//一个女人过来购买电脑
    		computer.accept(woman);
    		System.out.println("女人过来买需要花费:"+woman.getPrice());		
    	}
    }
    

    23、解释器模式(Interpreter)

    对于一些固定文法构建一个解释句子的解释器。在实际开发中,我们很少会去开发一个解释器,因为涉及到的知识点比较复杂。所以我们只需要了解即可,我们通过加减法的例子去了解它

    角色:

    • Expression 抽象化解释器
    • Context 上下文,用于存放变量和值
    • Addition 加法解释器(具体解释器)
    • Minus 减法解释器(具体解释器)
    • Variable 变量解释器 (具体解释器),它主要和上下文打交道(获取变量指定的值)

    解释器模式(小庄)

    //抽象化解释器
    abstract class Expression {
    	//解释方法
    	public abstract int interpret(Context context);
    }
    //导入相关的包
    import java.util.HashMap;
    //上下文
    class Context {
    	//定义一个键值对的容器
    	private HashMap<Variable,Integer> map=new HashMap<>();
    	//把变量名和值存放到容器中
    	public void addVariable(Variable v,Integer x) {
    		map.put(v, x);
    	}
    	//通过变量名获取值
    	public int getVlaue(Variable v) {
    		return map.get(v);
    	}
    }
    //变量类,继承解释器
    class Variable extends Expression{
    	//给每个变量定义一个名字
    	String name;
    	//通过构造方法给变量名赋值
    	public Variable(String name) {
    		this.name=name;
    	}
    	//变量解释方法,通过变量获取值
    	@Override
    	public int interpret(Context context) {
    		//通过变量名获取上下文的容器中对应的值
    		return context.getVlaue(this);
    	}
    	//重写toString()方法
    	@Override
    	public String toString() {
    		return name;
    	}
    }
    //具体解释器,加法器
    class Addition extends Expression{
    	//定义两个解释器的类,表示传进来的变量
    	private Expression left;
    	private Expression right;
    	//存放进去的是变量
    	public Addition(Expression left,Expression right) {
    		this.left=left;
    		this.right=right;
    	}
    	//进行加法运算
    	@Override
    	public int interpret(Context context) {
    		//通过变量的解释器,获取对于的值,进行运算
    		return left.interpret(context)+right.interpret(context);
    	}
    	@Override
    	public String toString() {
    		//重写toString方法
    		return "("+left.toString()+"+"+right.toString()+")";
    	}
    }
    //具体解释器,模拟减法器
    class Minus extends Expression{
    	//定义两个解释器变量
    	private Expression left;
    	private Expression right;
    	//通过构造函数的方式传入两个变量
    	public Minus(Expression left,Expression right) {
    		this.left=left;
    		this.right=right;
    	}
    	//解释方法
    	@Override
    	public int interpret(Context context) {
    		//通过变量的解释器,获取对于的值,进行运算
    		return left.interpret(context)-right.interpret(context);
    	}
    	@Override
    	public String toString() {
    		//重写toString()方法
    		return "("+left.toString()+"-"+right.toString()+")";
    	}
    }
    //测试类
    public class Client {
    	public static void main(String[] args) {
    		//创建上下文
    		Context context=new Context();
    		//创建变量
    		Variable a=new Variable("a");
    		Variable b=new Variable("b");
    		Variable c=new Variable("c");
    		Variable d=new Variable("d");
    		//将变量赋予值,并保存到上下文
    		context.addVariable(a, 1);
    		context.addVariable(b, 2);
    		context.addVariable(c, 3);
    		context.addVariable(d, 4);
    		//设置表达式
    		Expression expression=new Addition(a,new Minus(b,new Addition(c,d)));
    		//对表达式进行解释
    		int result=expression.interpret(context);
    		//打印输出结果
    		System.out.println(expression.toString()+"="+result);
    	}
    }
    

    结语

    通过上面的学习,我们发现很多设计模式都十分相似,其实这很正常,因为这些设计模式都是基于Java的继承、多态、聚合、组合等方式完成的。我们应该学习这些模式的设计思想,它们适合在什么场景,并且我们不需要归根到底是哪个设计模式,只要我们类的设计的合理,达到我们的需求即可。通过学习这个知识点,我们能够更加深入的理解开发框架的底层设计原理,有助于我们后面的学习。
    我是小庄,欢迎大家和我一起成长。

    展开全文
  • Java常见设计模式总结

    万次阅读 多人点赞 2021-09-18 17:18:54
    项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现实中都有相应的原理来与之对应,每种模式描述了一在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。...

     一、设计模式总述:

    1、什么是设计模式:

            设计模式是一套经过反复使用的代码设计经验,目的是为了重用代码、让代码更容易被他人理解、保证代码可靠性。 设计模式于己于人于系统都是多赢的,它使得代码编写真正工程化,它是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现实中都有相应的原理来与之对应,每种模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。总体来说,设计模式分为三大类:

    • 创建型模式:共5种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式
    • 结构型模式:共7种:适配器模式、装饰器模式、代理模式、桥接模式、外观模式、组合模式、享元模式
    • 行为型模式:共11种:策略模式、模板方法模式、观察者模式、责任链模式、访问者模式、中介者模式、迭代器模式、命令模式、状态模式、备忘录模式、解释器模式

    其实还有两类:并发型模式和线程池模式,用一个图片来整体描述一下:

    2、设计模式的六大原则:

    (1)开闭原则 (Open Close Principle) :

            开闭原则指的是对扩展开放,对修改关闭。在对程序进行扩展的时候,不能去修改原有的代码,想要达到这样的效果,我们就需要使用接口或者抽象类

    (2)依赖倒转原则 (Dependence Inversion Principle):

            依赖倒置原则是开闭原则的基础,指的是针对接口编程,依赖于抽象而不依赖于具体

    (3)里氏替换原则 (Liskov Substitution Principle) :

            里氏替换原则是继承与复用的基石,只有当子类可以替换掉基类,且系统的功能不受影响时,基类才能被复用,而子类也能够在基础类上增加新的行为。所以里氏替换原则指的是任何基类可以出现的地方,子类一定可以出现。

            里氏替换原则是对 “开闭原则” 的补充,实现 “开闭原则” 的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏替换原则是对实现抽象化的具体步骤的规范。

    (4)接口隔离原则 (Interface Segregation Principle):

            使用多个隔离的接口,比使用单个接口要好,降低接口之间的耦合度与依赖,方便升级和维护方便

    (5)迪米特原则 (Demeter Principle):

            迪米特原则,也叫最少知道原则,指的是一个类应当尽量减少与其他实体进行相互作用,使得系统功能模块相对独立,降低耦合关系。该原则的初衷是降低类的耦合,虽然可以避免与非直接的类通信,但是要通信,就必然会通过一个“中介”来发生关系,过分的使用迪米特原则,会产生大量的中介和传递类,导致系统复杂度变大,所以采用迪米特法则时要反复权衡,既要做到结构清晰,又要高内聚低耦合。

    (6)合成复用原则 (Composite Reuse Principle):

            尽量使用组合/聚合的方式,而不是使用继承。

    二、Java的23种设计模式:

            接下来我们详细介绍Java中23种设计模式的概念,应用场景等情况,并结合他们的特点及设计模式的原则进行分析

    1、创建型-工厂方法模式:

    工厂方法模式分为三种:

    (1)简单工厂模式:

    建立一个工厂类,并定义一个接口对实现了同一接口的产品类进行创建。首先看下关系图:

    (2)工厂方法模式:

    工厂方法模式是对简单工厂模式的改进,简单工厂的缺陷在于不符合“开闭原则”,每次添加新产品类就需要修改工厂类,不利于系统的扩展维护。而工厂方法将工厂抽象化,并定义一个创建对象的接口。每增加新产品,只需增加该产品以及对应的具体实现工厂类,由具体工厂类决定要实例化的产品是哪个,将对象的创建与实例化延迟到子类,这样工厂的设计就符合“开闭原则”了,扩展时不必去修改原来的代码。UML关系图如下:

     (3)静态工厂方法模式:

    静态工厂模式是将工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。

    工厂方法模式详情文章:Java设计模式之创建型:工厂模式详解(简单工厂+工厂方法+抽象工厂)

    2、创建型-抽象工厂模式:

            抽象工厂模式主要用于创建相关对象的家族。当一个产品族中需要被设计在一起工作时,通过抽象工厂模式,能够保证客户端始终只使用同一个产品族中的对象;并且通过隔离具体类的生成,使得客户端不需要明确指定具体生成类;所有的具体工厂都实现了抽象工厂中定义的公共接口,因此只需要改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。

            但该模式的缺点在于添加新的行为时比较麻烦,如果需要添加一个新产品族对象时,需要更改接口及其下所有子类,这必然会带来很大的麻烦。

            UML结构图如下:

    抽象工厂模式详情:Java设计模式之创建型:工厂模式详解(简单工厂+工厂方法+抽象工厂)

    3、创建型-建造者模式:

             建造者模式将复杂产品的创建步骤分解在在不同的方法中,使得创建过程更加清晰,从而更精确控制复杂对象的产生过程;通过隔离复杂对象的构建与使用,也就是将产品的创建与产品本身分离开来,使得同样的构建过程可以创建不同的对象;并且每个具体建造者都相互独立,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。UML结构图如下:

     建造者模式详情:Java设计模式之创建型:建造者模式

    4、创建型-单例模式:

            单例模式可以确保系统中某个类只有一个实例,该类自行实例化并向整个系统提供这个实例的公共访问点,除了该公共访问点,不能通过其他途径访问该实例。单例模式的优点在于:

    • 系统中只存在一个共用的实例对象,无需频繁创建和销毁对象,节约了系统资源,提高系统的性能
    • 可以严格控制客户怎么样以及何时访问单例对象。

    单例模式的写法有好几种,主要有三种:懒汉式单例、饿汉式单例、登记式单例。

    单例模式详情:Java设计模式之创建型:单例模式

    5、创建型-原型模式:

            原型模式也是用于对象的创建,通过将一个对象作为原型,对其进行复制克隆,产生一个与源对象类似的新对象。UML类图如下:

     在 Java 中,原型模式的核心是就是原型类 Prototype,Prototype 类需要具备以下两个条件:

    • 实现 Cloneable 接口:
    • 重写 Object 类中的 clone() 方法,用于返回对象的拷贝;

    Object 类中的 clone() 方法默认是浅拷贝,如果想要深拷贝对象,则需要在 clone() 方法中自定义自己的复制逻辑。

    • 浅复制:将一个对象复制后,基本数据类型的变量会重新创建,而引用类型指向的还是原对象所指向的内存地址。
    • 深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。

            使用原型模式进行创建对象不仅简化对象的创建步骤,还比 new 方式创建对象的性能要好的多,因为 Object 类的 clone() 方法是一个本地方法,直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显;

    原型模式详情:Java设计模式之创建型:原型模式

            

            上面我们介绍了5种创建型模式,下面我们就开始介绍下7种结构型模式:适配器模式、装饰模式、代理模式、外观模式、桥接模式、组合模式、享元模式。其中对象的适配器模式是各种模式的起源,如下图:

    6、结构型-适配器模式:

            适配器模式主要用于将一个类或者接口转化成客户端希望的格式,使得原本不兼容的类可以在一起工作,将目标类和适配者类解耦;同时也符合“开闭原则”,可以在不修改原代码的基础上增加新的适配器类;将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性,但是缺点在于更换适配器的实现过程比较复杂。

            所以,适配器模式比较适合以下场景:

    • (1)系统需要使用现有的类,而这些类的接口不符合系统的接口。
    • (2)使用第三方组件,组件接口定义和自己定义的不同,不希望修改自己的接口,但是要使用第三方组件接口的功能。

    下面有个非常形象的例子很好地说明了什么是适配器模式:

    适配器模式的主要实现有三种:类的适配器模式、对象的适配器模式、接口的适配器模式。三者的使用场景如下:

    • 类的适配器模式:当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可。
    • 对象的适配器模式:当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个Wrapper类,持有原类的一个实例,在Wrapper类的方法中,调用实例的方法就行。
    • 接口的适配器模式:当不希望实现一个接口中所有的方法时,可以创建一个抽象类Wrapper,实现所有方法,我们写别的类的时候,继承抽象类即可。

    适配器模式详情:Java设计模式之结构型:适配器模式

    7、结构型-装饰器模式:

            装饰器模式可以动态给对象添加一些额外的职责从而实现功能的拓展,在运行时选择不同的装饰器,从而实现不同的行为;比使用继承更加灵活,通过对不同的装饰类进行排列组合,创造出很多不同行为,得到功能更为强大的对象;符合“开闭原则”,被装饰类与装饰类独立变化,用户可以根据需要增加新的装饰类和被装饰类,在使用时再对其进行组合,原有代码无须改变。装饰器模式的UML结构图如下:

            但是装饰器模式也存在缺点,首先会产生很多的小对象,增加了系统的复杂性,第二是排错比较困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。

    装饰器模式详情:Java设计模式之结构型:装饰器模式

    8、结构型-代理模式:

            代理模式的设计动机是通过代理对象来访问真实对象,通过建立一个对象代理类,由代理对象控制原对象的引用,从而实现对真实对象的操作。在代理模式中,代理对象主要起到一个中介的作用,用于协调与连接调用者(即客户端)和被调用者(即目标对象),在一定程度上降低了系统的耦合度,同时也保护了目标对象。但缺点是在调用者与被调用者之间增加了代理对象,可能会造成请求的处理速度变慢。UML结构图如下:

    代理模式详情:Java设计模式之结构型:代理模式

    9、结构型-桥接模式:

            桥接模式将系统的抽象部分与实现部分分离解耦,使他们可以独立的变化。为了达到让抽象部分和实现部分独立变化的目的,桥接模式使用组合关系来代替继承关系,抽象部分拥有实现部分的接口对象,从而能够通过这个接口对象来调用具体实现部分的功能。也就是说,桥接模式中的桥接是一个单方向的关系,只能够抽象部分去使用实现部分的对象,而不能反过来。 

            桥接模式符合“开闭原则”,提高了系统的可拓展性,在两个变化维度中任意扩展一个维度,都不需要修改原来的系统;并且实现细节对客户不透明,可以隐藏实现细节。但是由于聚合关系建立在抽象层,要求开发者针对抽象进行编程,这增加系统的理解和设计难度。桥接模式的UML结构图如下:

            就像在Java中我们使用 JDBC 连接数据库时,在各个数据库之间进行切换,基本不需要动太多的代码,原因就是使用了桥接模式,JDBC 提供统一接口,每个数据库提供各自的实现,然后由桥接类创建一个连接数据库的驱动,使用某一个数据库的时候只需要切换一下就行。JDBC 的结构图如下:

             在 JDBC 中,桥接模式的实现化角色 (Implementor) 为的 Driver 接口,具体实现化 (Concrete Implementor) 角色对应 MysqlDriver、OracleDriver 和 MariadbDriver,扩展抽象化 (Refined Abstraction) 角色对应 DriverManager,不具有抽象化 (Abstraction) 角色作为扩展抽象化角色的父类。

    桥接模式详情:Java设计模式之结构型:桥接模式

    10、结构型-外观模式:

            外观模式通过对客户端提供一个统一的接口,用于访问子系统中的一群接口。使用外观模式有以下几点好处:

    (1)更加易用:使得子系统更加易用,客户端不再需要了解子系统内部的实现,也不需要跟众多子系统内部的模块进行交互,只需要跟外观类交互就可以了;

    (2)松散耦合:将客户端与子系统解耦,让子系统内部的模块能更容易扩展和维护。

    (3)更好的划分访问层次:通过合理使用 Facade,可以更好地划分访问的层次,有些方法是对系统外的,有些方法是系统内部使用的。把需要暴露给外部的功能集中到门面中,这样既方便客户端使用,也很好地隐藏了内部的细节。

            但是如果外观模式对子系统类做太多的限制则减少了可变性和灵活性,所以外观模式适用于为复杂子系统提供一个简单接口,提高系统的易用性场景 以及 引入外观模式将子系统与客户端进行解耦,提高子系统的独立性和可移植性。

            外观模式的UML结构图如下:

    外观模式详情: Java设计模式之结构型:外观模式

    11、结构型-组合模式:

            组合模式将叶子对象和容器对象进行递归组合,形成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性,能够像处理叶子对象一样来处理组合对象,无需进行区分,从而使用户程序能够与复杂元素的内部结构进行解耦。

            组合模式最关键的地方是叶子对象和组合对象实现了相同的抽象构建类,它既可表示叶子对象,也可表示容器对象,客户仅仅需要针对这个抽象构建类进行编程,这就是组合模式能够将叶子节点和对象节点进行一致处理的原因。组合模式的UML结构图如下:

    组合模式详情: Java设计模式之结构型:组合模式

    12、结构型-享元模式:

            享元模式通过共享技术有效地支持细粒度、状态变化小的对象复用,当系统中存在有多个相同的对象,那么只共享一份,不必每个都去实例化一个对象,极大地减少系统中对象的数量,从而节省资源。

            享元模式的核心是享元工厂类,享元工厂类维护了一个对象存储池,当客户端需要对象时,首先从享元池中获取,如果享元池中存在对象实例则直接返回,如果享元池中不存在,则创建一个新的享元对象实例返回给用户,并在享元池中保存该新增对象,这点有些单例的意思。

            工厂类通常会使用集合类型来保存对象,如 HashMap、Hashtable、Vector 等等,在 Java 中,数据库连接池、线程池等都是用享元模式的应用。

            享元模式的UML结构图如下:

             Java 中,String 类型就是使用享元模式,String 对象是 final 类型,对象一旦创建就不可改变。而 Java 的字符串常量都是存在字符串常量池中的,JVM 会确保一个字符串常量在常量池中只有一个拷贝。

            而且提到共享池,我们也很容易联想到 Java 里面的JDBC连接池,通过连接池的管理,实现了数据库连接的共享,不需要每一次都重新创建连接,节省了数据库重新创建的开销,提升了系统的性能!

    享元模式详情:Java设计模式之结构型:享元模式

            前面我们介绍了7种结构型设计模式,接下来我们介绍一下11种行为型设计模式:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。先来张图,看看这11中模式的关系:

     13、行为型-策略模式:

            将类中经常改变或者可能改变的部分提取为作为一个抽象策略接口类,然后在类中包含这个对象的实例,这样类实例在运行时就可以随意调用实现了这个接口的类的行为。

            比如定义一系列的算法,把每一个算法封装起来,并且使它们可相互替换,使得算法可独立于使用它的客户而变化,这就是策略模式。UML结构图如下:

            策略模式的优点在于可以动态改变对象的行为;但缺点是会产生很多策略类,并且策略模式的决定权在用户,系统只是提供不同算法的实现,所以客户端必须知道所有的策略类,并自行决定使用哪一个策略类; 

            策略模式适用用于以下几种场景:

    • (1)应用程序需要实现特定的功能服务,而该程序有多种实现方式使用,所以需要动态地在几种算法中选择一种
    • (2)一个类定义了多种行为算法,并且这些行为在类的操作中以多个条件语句的形式出现,就可以将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。

    策略模式详情:Java设计模式之行为型:策略模式

    14、行为型-模板方法:

            模板方法是基于继承实现的,在抽象父类中声明一个模板方法,并在模板方法中定义算法的执行步骤(即算法骨架)。在模板方法模式中,可以将子类共性的部分放在父类中实现,而特性的部分延迟到子类中实现,只需将特性部分在父类中声明成抽象方法即可,使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤,不同的子类可以以不同的方式来实现这些逻辑。

            模板方法模式的优点在于符合“开闭原则”,也能够实现代码复用,将不变的行为转移到父类,去除子类中的重复代码。但是缺点是不同的实现都需要定义一个子类,导致类的个数的增加使得系统更加庞大,设计更加抽象。模板方法模式的UML图如下:

    模板方法详情:Java设计模式之行为型:模板方法模式

    15、行为型-责任链模式:

            职责链可以将请求的处理者组织成一条链,并将请求沿着链传递,如果某个处理者能够处理请求则处理,否则将该请求交由上级处理。客户端只需将请求发送到职责链上,无须关注请求的处理细节,通过职责链将请求的发送者和处理者解耦了,这也是职责链的设计动机。        

           职责链模式可以简化对象间的相互连接,因为客户端和处理者都没有对方明确的信息,同时处理者也不知道职责链中的结构,处理者只需保存一个指向后续者的引用,而不需要保存所有候选者的引用。

            另外职责链模式增加了系统的灵活性,我们可以任意增加或更改处理者,甚至更改处理者的顺序,不过有可能会导致一个请求无论如何也得不到处理,因为它可能被放置在链末端。

    所以责任链模式有以下几个优点:

    • (1)降低耦合度,将请求的发送者和接收者解耦。反映在代码上就是不需要在类中写很多丑陋的 if….else 语句,如果用了职责链,相当于我们面对一个黑箱,只需将请求递交给其中一个处理者,然后让黑箱内部去负责传递就可以了。
    • (2)简化了对象,使得对象不需要链的结构。
    • (3)增加系统的灵活性,通过改变链内的成员或者调动他们的次序,允许动态地新增或者删除处理者
    • (4)增加新的请求处理类很方便。

    但是责任链模式也存在一些缺点:

    • (1)不能保证请求一定被成功处理
    • (2)系统性能将受到一定影响,并且可能会造成循环调用。
    • (3)可能不容易观察运行时的特征,而且在进行代码调试时不太方便,有碍于除错。

            责任链模式的UML结构图如下:

    责任链模式详情:Java设计模式之行为型:责任链模式

    16、行为型-观察者模式:

            观察者模式又称为 发布-订阅模式,定义了对象之间一对多依赖关系,当目标对象(被观察者)的状态发生改变时,它的所有依赖者(观察者)都会收到通知。一个观察目标可以对应多个观察者,而这些观察者之间没有相互联系,所以能够根据需要增加和删除观察者,使得系统更易于扩展,符合开闭原则;并且观察者模式让目标对象和观察者松耦合,虽然彼此不清楚对方的细节,但依然可以交互,目标对象只知道一个具体的观察者列表,但并不认识任何一个具体的观察者,它只知道他们都有一个共同的接口。

            但观察者模式的缺点在于如果存在很多个被观察者的话,那么将需要花费一定时间通知所有的观察者,如果观察者与被观察者之间存在循环依赖的话,那么可能导致系统崩溃,并且观察者模式没有相应的机制让观察者知道被观察对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。观察者模式的UML结构图如下:

     观察者模式详情:Java设计模式之行为型:观察者模式

    17、行为型-访问者模式:

            访问者模式就是一种分离对象数据结构与行为 (基于数据结构的操作) 的方法,通过这种分离,达到为一个被访问者动态添加新的操作而无需做其它修改的效果,使得添加作用于这些数据结构的新操作变得简单,并且不需要改变各数据结构,为不同类型的数据结构提供多种访问操作方式,这样是访问者模式的设计动机。

            除了使新增访问操作变得更加简单,也能够在不修改现有类的层次结构下,定义该类层次结构的操作,并将有关元素对象的访问行为集中到一个访问者对象中,而不是分散搞一个个的元素类中。

           但访问者模式的缺点在于让增加新的元素类变得困难,每增加一个新的元素类都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作,违背了“开闭原则”的要求;

            所以访问者模式适用于对象结构中很少改变,但经常需要在此对象结构上定义新的操作的系统,使得算法操作的增加变得简单;或者需要对一个对象结构中进行很多不同并且不相关的操作,并且需要避免让这些操作污染这些对象,也不希望在增加新操作时修改这些类的场景。

            访问者模式的UML结构图如下:

            从上面的 UML 结构图中我们可以看出,访问者模式主要分为两个层次结构,一个是访问者层次结构,提供了抽象访问者和具体访问者,主要用于声明一些操作;一个是元素层次结构,提供了抽象元素和具体元素,主要用于声明 accept 操作;而对象结构 ObjectStructure 作为两者的桥梁,存储了不同类型的对象,以便不同的访问者来访问,相同访问者可以以不同的方式访问不同的元素,所以在访问者模式中增加新的访问者无需修改现有代码,可扩展行强。

            在访问者模式使用了双分派技术,所谓双分派技术就是在选择方法的时候,不仅仅要根据消息接收者的运行时区别,还要根据参数的运行时区别。在访问者模式中,客户端将具体状态当做参数传递给具体访问者,这里完成第一次分派,然后具体访问者作为参数的“具体状态”中的方法,同时也将自己this作为参数传递进去,这里就完成了第二次分派。双分派意味着得到的执行操作决定于请求的种类和接受者的类型。

     访问者模式详情:Java设计模式之行为型:访问者模式

    18、行为型-中介者模式:

             中介者模式通过中介者对象来封装一系列的对象交互,将对象间复杂的关系网状结构变成结构简单的以中介者为核心的星形结构,对象间一对多的关联转变为一对一的关联,简化对象间的关系,便于理解;各个对象之间的关系被解耦,每个对象不再和它关联的对象直接发生相互作用,而是通过中介者对象来与关联的对象进行通讯,使得对象可以相对独立地使用,提高了对象的可复用和系统的可扩展性。

            在中介者模式中,中介者类处于核心地位,它封装了系统中所有对象类之间的关系,除了简化对象间的关系,还可以对对象间的交互进行进一步的控制。中介者模式的UML结构图如下:

            但是,中介者对象封装了对象之间的关联关系,导致中介者对象变得比较庞大复杂,所承担的责任也比较多,维护起来也比较困难,它需要知道每个对象和他们之间的交互细节,如果它出问题,将会导致整个系统都会出问题。

    中介者模式详情:Java设计模式之行为型:中介者模式

    19、行为型-命令模式:

            命令模式的本质是将请求封装成对象,将发出命令与执行命令的责任分开,命令的发送者和接收者完全解耦,发送者只需知道如何发送命令,不需要关心命令是如何实现的,甚至是否执行成功都不需要理会。命令模式的关键在于引入了抽象命令接口,发送者针对抽象命令接口编程,只有实现了抽象命令接口的具体命令才能与接收者相关联。

            使用命令模式的优势在于降低了系统的耦合度,而且新命令可以很方便添加到系统中,也容易设计一个组合命令。但缺点在于会导致某些系统有过多的具体命令类,因为针对每一个命令都需要设计一个具体命令类。

            命令模式的UML结构图如下:

    命令模式详情: Java设计模式之行为型:命令模式

    20、行为型-状态模式:

            状态模式,就是允许对象在内部状态发生改变时改变它的行为,对象看起来就好像修改了它的类,也就是说以状态为原子来改变它的行为,而不是通过行为来改变状态。

            当对象的行为取决于它的属性时,我们称这些属性为状态,那该对象就称为状态对象。对于状态对象而言,它的行为依赖于它的状态,比如要预订房间,只有当该房间空闲时才能预订,想入住该房间也只有当你预订了该房间或者该房间为空闲时。对于这样的一个对象,当它的外部事件产生互动的时候,其内部状态就会发生变化,从而使得他的行为也随之发生变化。

            状态模式的UML结构图如下:

     从上面的UML结构图我们可以看出状态模式的优点在于:

    (1)封装了转换规则,允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块

    (2)将所有与状态有关的行为放到一个类中,可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。 

    但是状态模式的缺点在于:

    (1)需要在枚举状态之前需要确定状态种类

    (2)会导致增加系统类和对象的个数。

    (3)对 “开闭原则” 的支持并不友好,新增状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态;而且修改某个状态类的行为也需修改对应类的源代码。

    所以状态模式适用于:代码中包含大量与对象状态有关的条件语句,以及对象的行为依赖于它的状态,并且可以根据它的状态改变而改变它的相关行为。

    状态模式详情:Java设计模式之行为型:状态模式

    21、行为型-备忘录模式:

            备忘录模式提供了一种恢复状态的机制,在不破坏封装的前提下,捕获对象的某个时刻内部状态,并保存在该对象之外,保证该对象能够恢复到某个历史状态;备忘录模式将保存的细节封装在备忘录中,除了创建它的创建者之外其他对象都不能访问它,并且实现了即使要改变保存的细节也不影响客户端。但是备忘录模式都是多状态和多备份的,会早用较多的内存,消耗资源。备忘录模式的额UML结构图如下:

             备忘录模式的核心就是备忘录 Memento,在备忘录中存储的就是原发器 Originator 的部分或者所有的状态信息,而这些状态信息是不能够被其他对象所访问的,也就是说我们是不能使用备忘录之外的对象来存储这些状态信息,如果暴漏了内部状态信息就违反了封装的原则,故备忘录除了原发器外其他对象都不可以访问。所以为了实现备忘录模式的封装,我们需要对备忘录的访问做些控制:

    (1)对原发器:可以访问备忘录里的所有信息。

    (2)对负责人 caretaker:不可以访问备忘录里面的数据,但是他可以保存备忘录并且可以将备忘录传递给其他对象。

    (3)其他对象:不可访问也不可以保存,它只负责接收从负责人那里传递过来的备忘录同时恢复原发器的状态。

    备忘录模式详情:Java设计模式之行为型:备忘录模式

    22、行为型-迭代器模式:

            迭代器模式提供一种访问集合中的各个元素,而不暴露其内部表示的方法。将在元素之间游走的职责交给迭代器,而不是集合对象,从而简化集合容器的实现,让集合容器专注于在它所应该专注的事情上,更加符合单一职责原则,避免在集合容器的抽象接口层中充斥着各种不同的遍历操作。迭代器模式的UML结构图如下:

    迭代器模式详情:Java设计模式之行为型:迭代器模式

    23、行为型-解释器模式:

            解释器模式,就是定义语言的文法,并建立一个解释器来解释该语言中的句子,通过构建解释器,解决某一频繁发生的特定类型问题实例。

            解释器模式描述了如何构成一个简单的语言解释器,主要应用在使用面向对象语言开发的编译器中,它描述了如何为简单的语言定义一个文法,如何在该语言中表示一个句子,以及如何解释这些句子。    

            解释器模式中除了能够使用文法规则来定义一个语言,还能通过使用抽象语法树来更加直观表示、更好地地表示一个语言的构成,每一颗抽象语法树对应一个语言实例。抽象语法树描述了如何构成一个复杂的句子,通过对抽象语法树的分析,可以识别出语言中的终结符和非终结符类。 在解释器模式中由于每一种终结符表达式、非终结符表达式都会有一个具体的实例与之相对应,所以系统的扩展性比较好。

            解释器模式的UML如下:

     解释器模式详情:Java设计模式之行为型:解释器模式


    相关推荐阅读:

    Spring常见面试题总结

    SpringMVC常见面试题总结

    Mybatis常见面试题总结

    MySQL常见面试题总结

    Redis常见面试题总结

    RabbitMQ消息队列常见面试题总结

    ElasticSearch搜索引擎常见面试题总结

    计算机网络常见面试题总结

    操作系统常见面试题总结

    Java基础、集合、多线程常见面试题总结

    Java虚拟机常见面试题总结

    Java常见设计模式总结

    海量数据处理的方法总结


    参考文章:

    Java之美[从菜鸟到高手演变]之设计模式

    Java之美[从菜鸟到高手演变]之设计模式二

    Java之美[从菜鸟到高手演变]之设计模式三

    Java之美[从菜鸟到高手演变]之设计模式四

    展开全文
  • 学习java设计模式有用吗?懂这六个原则,编程更轻松1.开闭原则(Open Close Principle)开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一热插拔的效果。简单来说:...

    学习java设计模式有用吗?懂这六个原则,编程更轻松

    1.开闭原则(Open Close Principle)

    开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简单来说:就是为了使程序的扩展性好,易于维护和升级。

    2.接口隔离原则(Interface Segregation Principle)

    这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。在开发过程当中回尽量地去降低依赖,降低耦合。

    3.迪米特法则(Demeter Principle)

    一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。

    4.单一职责原则(Single Responsibility Principle)

    单一职责的定义:应该有且只有一个原因引起类的变更。换句话说就是一个接口只做一件事,即一个职责一个接口。

    但是困难的是划分职责时并没有一个标准,最终都是需要从实际的项目去考虑。我们在设计的时候,尽量单一,然后对于其实现类就要多方面的考虑。不能死套单一职责原则,否则会增加很多类,给维护带来不便。

    5.里氏代换原则(Liskov Substitution Principle)

    里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。

    LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。

    里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

    6.依赖倒转原则(Dependence Inversion Principle)

    这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。

    java中抽象指接口或抽象类,两者都不能直接被实例化的;细节就是实现类,实现接口或者集成抽象类而产生的也就细节,也就是可以可以加上yige 关键字new产生的对象。高层模块就是调用端,低层模块就是具体实现类。

    依赖倒置原则在java中表现就是,模块间依赖通过抽象发生,实现类之间不发生直接依赖关系,其依赖关系是通过接口或者抽象类产生的。如果类与类直接依赖细节,那么就会直接耦合。如此一来当修改时,就会同时修改依赖者代码,这样限制了可拓展性。

    Java设计模式在代码量很少的情况下,是没什么用的,只会白白增加代码量。但当代码量很多时,设计模式便能发挥作用,所以掌握设计模式是很有用的!

    更多经验分享,更多java资料可以私信我,我分享给大家!

    展开全文
  • 设计模式七大原则一、设计模式的目的二、设计模式的七大原则1.单一职责原则(1)基本介绍2.接口隔离原则(1)基本介绍(2)应用传统方法的问题和使用接口隔离原则改进3.依赖倒转原则(1)基本介绍(2)依赖关系传递的...

    一、设计模式的目的

    编写软件的过程中,程序员面临着来自耦合性,内聚性以及可维护性,可扩展性,重用性,灵活性等多方面的挑战,设计模式是为了让程序具有更好的
    1.代码重用性(即:相同的代码,不用,多次编写)
    2.可读性(即:代码编程规范,便于其他程序员的阅读和理解)
    3.可扩展性(即:当我们增加新的功能时,非常的方便,也称为可维护性)
    4.可靠性(即:当我们新增新的功能后,对原来的功能没有影响)
    5.使程序呈现高内聚,低耦合的特性

    设计模式包含了面向对象的精髓,“懂了设计模式,你就懂了面向对象分析和设计的精要。

    二、设计模式的七大原则

    设计模式原则,其实就是程序员在编程时,应当遵守的原则,也是各种设计模式的基础。

    1.单一职责原则

    (1)基本介绍

    对类来说,即一个类应该只负责一项职责。如类A负责两个不同职责:职责1,职责2.当职责1需要变更而改变A时,可能造成职责2执行错误,所以需要将类A的粒度分解为A1,A2

    2.接口隔离原则

    (1)基本介绍

    1)客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上。
    2)先看一张图
    在这里插入图片描述
    3)类A通过接口Interface1依赖类B,类C通过接口Interface1依赖类D,如果接口Interface1对于类A和类C来说不是最小接口,那么类B和leiD必须去实现他们不需要的方法。
    4)按隔离原则应当这样处理:
    将接口Interface1拆分为独立的几个接口(这里我们拆分为3个接口),类A和类C分别与他们需要的接口建立依赖关系,也就是采用接口隔离原则

    (2)应用传统方法的问题和使用接口隔离原则改进

    1)类A通过接口Interface1依赖类B,类C通过接口Interface1依赖类D,如果接口Interface1对于类A和类C来说不是最小接口,那么类B和类D必须去实现他们不需要的方法
    2)将接口Interface1拆分为独立的几个接口,类A和类C分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则。
    3)接口Interface1中出现的方法,根据实际情况拆分为三个接口
    在这里插入图片描述

    3.依赖倒转原则

    (1)基本介绍

    依赖倒转原则是指:
    1)高层模块不应该依赖低层模块,二者都应该依赖其抽象
    2)抽象不应该依赖细节,细节应该依赖抽象
    3)依赖倒转的中心思想是面向接口编程
    4)依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在Java中,抽象指的是接口或抽象类,细节就是具体的实现类。
    5)使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。

    (2)依赖关系传递的三种方式和应用案例

    1)接口传递
    2)构造方法传递
    3)setter方式传递

    (3)依赖倒转原则的注意事项和细节

    1)低层模块尽量都要有抽象类或接口,或者两者都没有,程序稳定性更好。
    2)变量的声明类型尽量是抽象类或接口,这样我们的变量引用和实际对象间,就存在一个缓冲层,利于程序扩展和优化。
    3)继承时遵循里氏替换原则

    4.里氏替换原则

    (1)OO中继承性的思考和说明

    1)继承包含这样一层含义:父类中凡是已经实现好的方法,实际上是在设定规范和契约,虽然它不强制要求所有的子类必须遵守这样的契约,但是如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏。
    2)继承在给程序涉及带来便利的同时,也带来了弊端。比如使用继承会给程序带来侵入性,程序的可移植性降低,增加对象间的耦合性,如果一个类被其他类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到的子类的功能都有可能产生故障。

    (2)基本介绍

    如果对每个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有程序P在所有对象o1都代换成o2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型,换句话说,所有引用基类的地方必须能透明地使用其子类的对象。
    在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法。
    里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,在适当情况下,可以通过聚合,组合,依赖来解决问题。

    5.开闭原则

    (1)基本介绍

    1)开闭原则是编程中最基础、最重要的设计原则
    2)一个软件实体如类,模块和函数应该对扩展开放(对提供方),对修改关闭(对使用方)。用抽象构建框架,用实现扩展细节。
    3)当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
    4)编程中遵循其他原则,以及使用设计模式的目的就是遵循开闭原则。

    6.迪米特法则

    (1)基本介绍

    1)一个对象应该对其他对象保持最少的了解
    2)类与类的关系越密切,耦合度越大
    3)迪米特法则又叫最少知道原则,即一个类对自己依赖的类知道的越少越好,也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供public方法,不对外泄露任何信息
    4)迪米特法则还有一个更简单的定义:只与直接的朋友通信
    5)直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间时朋友关系。耦合的方式很多,依赖,关联,组合,聚合等。其中,我们称出现成员变量,方法参数,方法返回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说。陌生的类最好不要以局部变量的形式出现在类的内部。

    (2)迪米特法则注意事项和细节

    1)迪米特法则的核心是降低类之间的耦合
    2)但是注意:由于每个类都减少了不必要的依赖,因此迪米特法则只是要求降低类间耦合关系,并不是要求完全没有依赖关系

    7.合成复用原则

    (1)基本介绍

    原则是尽量使用合成/聚合的方式,而不是使用继承

    三、设计模式核心思想

    1)找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要的代码混在一起
    2)针对接口编程,而不是针对实现编程。
    3)为了交互对象之间的松耦合设计而努力

    展开全文
  • 设计模式的目的: 让程序耦合性,内聚性,可维护性,可扩展性,重用性以及灵活性...设计模式七大原则: 1、单一职责原则 降低类的复杂度,一类只负责一项职责 提高类的可读性,可维护性 降低变更引起的...
  • 最近被敖丙的博客,被安利了一本书《重学java设计模式》,本文谨记录学习过程中的心得。 六大设计原则 书中总结得比较好,这里直接把原文的重点附上 单一职责原则 如果需要开发的一功能需求不是一次性的,且随着...
  • 第 2 章 面向对象设计原则 B A C D D C C D D “封装变化点”可对应“开闭原则”,“对接口进行编程”可对应“依赖倒转原则”,“多使用组合, 而不是继承”可对应“合成复用原则”。 类的粒度需满足...
  • java 设计模式原则有:开闭原则、 里氏代换原则、依赖倒转原则、接口隔离原则、迪米法特原则、合成复用原则。 1. 开闭原则 对扩展开放、对修改关闭。 在程序需要进行拓展的时候,不能去修改原有的代码,实现一热插...
  • java设计模式六大原则

    2021-01-29 21:03:22
    java设计模式六大原则 说到设计模式,它不是一种新的语言,也不是新的API,也不是新的语法, 设计模式,是前辈们,不断总结,不断打磨出得一种经验的总结,不同的设计模式适用不同的场景。 设计模式,公认的有23种,...
  • Java设计模式的6大原则:1.开闭原则开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一热插拔的效果。简单来说:就是为了使程序的扩展性好,易于维护和升级。????2....
  • 设计模式是为了让程序具有更好的 代码重用性、可读性、可扩展性、可靠性、使程序呈现高内聚、低耦合的特性 1.单一职责原则 对类来说,一类应该只负责一项职责(功能),这样可以降低类的复杂度,提高类的可读性可...
  • 而在设计模式概述里面,我们第一要明了的就是软件设计模式的产生背景。因为我们既然要学习软件设计模式,那么就得知道一下它的背景,我想这是毋庸置疑的。 "设计模式"最初并不是出现在软件设计中,而是被用于建筑...
  • 1.请列举出在 JDK 中几常用的设计模式?单例模式(Singleton pattern)用于 Runtime,Calendar 和其他的一些类中。工厂模式(Factory pattern)被用于各种不可变的类如 Boolean,像 Boolean.valueOf。观察者模式...
  • Java设计模式目录

    2021-03-09 05:39:02
    设计模式的六大原则1、开闭原则(Open Close Principle)开闭原则的意思是:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一热插拔的效果。简言之,是为了使程序的扩展性好,易于...
  • 问题由来:在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误,也可能会使我们不得不对整个...接口隔离原则5.迪米特法则6.开闭原则开闭原则是指对扩展开放,对...
  • 设计模式本质上就是“SOLID设计原则”在实际应用中的具体体现,我们在实际开发中要尽量面向抽象编程、面向接口编程。顾客->菜单适配器模式的定义:将某个类的接口转换为客户所需的类型。换句话说,适配器模式的...
  • 《J A V A设计模式》复习资料一、单项选择题1.设计模式起源于()A、机械设计B、建筑工程设计C、水利工程设计D、工业电力化设计2.“不要和陌生人说话”是()原则的通俗表述。A、接口隔离B、里氏替换C、依赖倒置D、...
  • ​里氏代换原则Java中的体现 前面说了里氏代换原则,那么现在说一下里氏代换原则Java中的使用。 里氏代换原则要求父类类型可以使用的,那么子类一定可以适用。因此子类必须具有基类的全部接口,并且可能更...
  • 2、设计原则设计原则是把一类中经常改变或者将来可能改变的部分提取出来,作为一接口(C++中可以用虚类),然后在类中包含这对象的实例,这样类的实例在运行时就可以随意调用实现了这接口的类的行为。...
  • 容错恢复机制容错恢复机制是应用程序开发中非常常见的功能。那么什么是容错恢复呢?简单点说就是:程序运行的时候,正常情况下应该按照某种方式来做,如果按照某种...举实际点的例子吧,比如在一系统中,所有对...
  • Java设计模式面试题

    2021-01-18 10:34:53
    1. 请列举出在JDK中几常用的设计模式? 单例模式:保证被创建一次,节省系统开销。 工厂模式(简单工厂、抽象工厂):解耦代码。 观察者模式:定义了对象之间的一对多的依赖,这样一来,当一对象改变时,它的...
  • 1、责任链模式(Chain of responsibility pattern): 通过责任链模式, 你可以为某个请求创建一对象链. 每对象依序检查此请求并对其进行处理或者将它传给链中的下一对象.2、命令模式(Command pattern): 将"请求...
  • 设计模式六大原则(4):接口隔离原则定义:客户端不应该依赖它不需要的接口;一类对另一类的依赖应该建立在最小的接口上。问题由来:类A通过接口I依赖类B,类C通过接口I依赖类D,如果接口I对于类A和类B来说不是...
  • 策略模式:策略模式是一种行为型模式,它将对象和行为分开,将行为定义为 一行为接口 和 具体行为的实现。策略模式最大的特点是行为的变化,行为之间可以相互替换。每if判断都可以理解为就是一策略。本模式...
  • Java设计模式

    2021-01-05 10:42:27
    文章结构设计原则常用模式总结策略模式 Strategy观察者模式 Observer装饰者模式 Decorator工厂方法模式 Factory Method抽象工厂模式 Abstract Factory单件模式 Singleton命令模式 Command适配器模式 Adapter外观模式...
  • Java设计模式面向对象设计原则面向对象设计原则概述七常用的面向对象设计原则单一职责原则开闭原则里氏代换原则依赖倒转原则接口隔离原则合成复用原则迪米特法则开闭原则是目标,里氏代换原则是基础,依赖倒转原则...
  • 1. 请列举出在JDK中几常用的设计模式?单例模式:保证被创建一次,节省系统开销。工厂模式(简单工厂、抽象工厂):解耦代码。观察者模式:定义了对象之间的一对多的依赖,这样一来,当一对象改变时,它的所有的...
  • Java设计模式的6大原则:1.开闭原则开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一热插拔的效果。简单来说:就是为了使程序的扩展性好,易于维护和升级。????2....
  • 设计模式六大原则(2):里氏替换原则肯定有不少人跟我刚看到这项原则的时候一样,对这个原则的名字充满疑惑。其实原因就是这项原则最早是在1988年,由麻省理工学院的一位姓里的女士(Barbara Liskov)提出来的。定义1:...
  • 文章目录Java设计模式Java设计模式的概述Java设计模式的目的设计模式七大原则单一职责原则(Single Responsibility Principle, SRP)基本介绍示例**非单一职责(错误示范)****遵循类单一职责**使用原则优点接口隔离...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 196,139
精华内容 78,455
关键字:

java设计模式的五个原则

java 订阅