java的7大设计原则和设计模式

设计模式

一、七大设计原则

设计原则是设计模式的基础

单一设计原则

  • 一个类之应该负责一项职责

  • 避免过多的使用if_else(耦合性过高)

接口隔离原则

  • 一个类对另外一个类的依赖,应该建立在最小的接口

  • 如果一个接口包含了很多方法,但是这个接口的实现类从逻辑或业务上只需要实现其中部分方法。

    那么这个接口就违背了接口隔离原则,可以考虑将此接口拆了

依赖倒转原则

  • 核心思想:面向接口编程,使用多态的特性:用接口去声明,真正使用时调用具体子类的具体实现
  • 抽象属于高层,比细节实现,要更加的稳定
  • 依赖关系传递的三种方式
    • 方法参数接口传递
    • 构造方法传递
    • set注入

里氏替换原则

  • 所有引用基类的地方必须能透明的使用其子类对象
  • 子类尽量不要重写父类的方法
    • 子类重写父类方法可能改变父类的行为,特别是这个被子类重写的方法还被父类的其它方法调用了,那么父类的其他方法在执行时,行为就完全不受控制了
  • 让原来的父类和子类都继承一个更通俗的基类,把原有的继承关系去掉,采用依赖,聚合,组合等关系替代
    • 也就是说,如果B类想使用A类中的方法,我们不让B类继承A类,但是我们传一个A类对象给B类,B类使用A类这个对象调用A类中的方法,这样B类和A类就没关系了,因此就不用担心方法被重写的问题了

开闭原则

  • 最重要、最基础的原则
    • 遵循其它原则的目的就是为了实现开闭原则
  • 当功能需求变化时,应当:对扩展开放(对提供方而言),对修改关闭(对使用方而言)。
  • 尽量通过扩展软件实体的行为来 实现变化,而不是通过修改已有代码

迪米特原则

  • 最少知道原则
  • 只与直接朋友通信(一个类中,如果要出现另外的类,最好只出现在以下3个位置)
    • 方法参数
    • 返回值
    • 成员变量
  • 一个类对自己依赖的类知道的越少越好
    • 对于被依赖的类,都尽量将逻辑封装在类自己的内部,对外提供public方法

合成复用原则

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

  • 依赖、聚合、组合 ( B类要用到A类中的方法)

    • 依赖

      class B{
      	void invokeA( A a){ ... }
      }
      
    • 聚合

      class B{
      	A a;
      	void set(A a){
      		this.a = a;
      	}
      }
      
    • 组合

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

二、类的关系与UML图

依赖

  • 只要A类有用到B类,那么就是A 依赖B ; 没有B类,A不能通过编译
  • A ---------->B

泛化

  • 就是继承关系
  • A —空三角形 B

实现

  • A -----空三角形 B

关联

  • 实线

聚合

  • 整体和部分可以分开
  • 实线 + 空心菱形

组合

  • 整体和部分不可分开,共生
  • 实现 + 实心菱形

三、23种设计模式

分类

  • 创建型模式

  • 结构型模式

  • 行为型模式

1. 单例模式

  • 饿汉式 1 可以用

    //饿汉式(静态变量)
    
    class Singleton {
    	
    	//1. 构造器私有化, 外部能new
    	private Singleton() {
    		
    	}
    	
    	//2.本类内部创建对象实例
    	private final static Singleton instance = new Singleton();
    	
    	//3. 提供一个公有的静态方法,返回实例对象
    	public static Singleton getInstance() {
    		return instance;
    	}
    	
    }
    

    如果该单例未被使用,则会造成内存浪费

  • 饿汉式 2 可以用

    //饿汉式(静态变量)
    
    class Singleton {
    	
    	//1. 构造器私有化, 外部能new
    	private Singleton() {
    		
    	}
    	
    
    	//2.本类内部创建对象实例
    	private  static Singleton instance;
    	
    	static { // 在静态代码块中,创建单例对象
    		instance = new Singleton();
    	}
    	
    	//3. 提供一个公有的静态方法,返回实例对象
    	public static Singleton getInstance() {
    		return instance;
    	}
    	
    }	
    
  • 懒汉式 1 不能使用

    class Singleton {
    	private static Singleton instance;
    	
    	private Singleton() {}
    	
    	//提供一个静态的公有方法,当使用到该方法时,才去创建 instance
    	//即懒汉式
    	public static Singleton getInstance() {
    		if(instance == null) {
    			instance = new Singleton();
    		}
    		return instance;
    	}
    }
    

    不安全

  • 懒汉式2 不推荐使用

    // 懒汉式(线程安全,同步方法)
    class Singleton {
    	private static Singleton instance;
    	
    	private Singleton() {}
    	
    	//提供一个静态的公有方法,加入同步处理的代码,解决线程安全问题
    	//即懒汉式
    	public static synchronized Singleton getInstance() {
    		if(instance == null) {
    			instance = new Singleton();
    		}
    		return instance;
    	}
    }
    

    安全,但因为加了同步效率不高

  • 懒汉式3 不能使用

    
    

    使用同步代码块,不能解决线程安全问题

  • 懒汉式4 推荐使用

    使用同步代码块,双重检查

    // 懒汉式(线程安全,同步方法)
    class Singleton {
    	private static volatile Singleton instance;
    	
    	private Singleton() {}
    	
    	//提供一个静态的公有方法,加入双重检查代码,解决线程安全问题, 同时解决懒加载问题
    	//同时保证了效率, 推荐使用
    	
    	public static synchronized Singleton getInstance() {
    		if(instance == null) {
    			synchronized (Singleton.class) {
    				if(instance == null) {
    					instance = new Singleton();
    				}
    			}
    			
    		}
    		return instance;
    	}
    }
    
  • 静态内部类 推荐使用

    • 外部类加载,静态内部类不会加载
    • 加载静态内部类时,在静态属性上实例化,同时类加载是线程安全的
    // 静态内部类完成, 推荐使用
    class Singleton {
    	private static volatile Singleton instance;
    	
    	//构造器私有化
    	private Singleton() {}
    	
    	//写一个静态内部类,该类中有一个静态属性 Singleton
    	private static class SingletonInstance {
    		private static final Singleton INSTANCE = new Singleton(); 
    	}
    	
    	//提供一个静态的公有方法,直接返回SingletonInstance.INSTANCE
    	
    	public static synchronized Singleton getInstance() {
    		
    		return SingletonInstance.INSTANCE;
    	}
    }
    
  • 枚举推荐使用

    //使用枚举,可以实现单例, 推荐
    enum Singleton {
    	INSTANCE; //属性
    	public void sayOK() {
    		System.out.println("ok~");
    	}
    }
    

2.工厂模式

  • 简单工厂模式

    • 定义一个创建对象的类,由这个类来封装实例化对象的代码

    • 其它类需要对象时,只要聚合该简单工厂类对象,调用该简单工厂类创建并返回对象即可;

    • 静态工厂

      • 同上,将工厂类创建对象的方法改为静态即可
class PizzaStore{ // 工厂类     
    public Pizza createPizza(String type){  // 工厂类创建产品的方法
        if(type="cheese"){     
            return new CheesePizza();
        }else if(type="veggie"){
            return new VeggiePizza();
        }
    }
}

abstract class Pizza{} // 抽象产品
class CheesePizza extends Pizza{} // 具体产品1
class VeggiePizza extends Pizza{} // 具体产品2

// 如果需要创建产品,那么只需要聚合该工厂类作为成员变量,那么使用工厂类对象的创建产品的方法创建产品了
// 将创建产品的细节封装到工厂类中,方便对创建实例时的统一管理(要修改的话,就修改他就行了)
  • 工厂方法模式
    • 定义一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类
    • 直接使用工厂子类,会调用抽象类中的方法(找父类),然后该方法中会调用子类具体实现
public abstract class PizzaStore{
    
    Pizza orderPizza(String type){
        Pizza pizza = createPizza(String type);// 调用子类
        
        pizza.prepare(); // 不变的部分(默认的行为)
        pizza.bake();
        pizza.cut();
        pizza.box();
    }
    abstract Pizza createPizza(); // 交给子类实现
}

// 子类工厂1
class NYPizzaStore{ 
    Pizza createPizza(String type){
        Pizza nyStylePizza = new NyStylePizza(); // 生产具体产品
        // 根据type...
        return nyStylePizza;
    }
}

public abstract class Pizza{ // 产品的抽象类
     void pizza.prepare(); 
     void pizza.bake();
     void pizza.cut();
     void pizza.box();
}

class NYStylePizza{ // 具体产品1
    void pizza.prepare(){/*...*/} 
     void pizza.bake()  {/*...*/}
     void pizza.cut()   {/*...*/}
     void pizza.box()   {/*...*/}
}

// 对于抽象类PizzaStore来说,它在orderPizza()方法里是直接面向接口的(并非具体实现),方便了松耦合
// 由客户端去决定该生产哪种产品,因此须找到具体的PizzaStore的子类,调用这个子类的创建对象的方法,这个子类就是具体工厂
// 如果需要扩展的话,那么只要新添子类去实现PizzaStore即可
  • 抽象工厂模式
    • 对工厂方法模式进一步抽象,直接将抽象方法定义在接口中,逻辑全部交给子类实现

3.策略模式

通过组合的方式,将需要实现的功能从本类中剥离出来成一个接口。

public class Duck{	
    
    // 少用继承(避免继承的局限性),多用组合。将功能的实现委托给其它具体实现类,使用接口引用具体行为。
	FlyBehavior flybehavior; 
    
    void setFlyBehavior(FlyBehavior flybehavior){
        this.flybehavior = flybehavior;
    }
    
    performFlyBehavior(){
        this.flybehavior.fly();  // 这样可以使得Duck的子类的行为灵活多变,且可扩展,易于修改
    }
    
}

interface FlyBehavior{ // 使用接口定义行为 ,将fly这种行为封装在一个类中 ,这样就可以让Duck类能引用一族算法
    void fly;
}

比如:spring中就在解析xml文件的时候,就引入了一个BeDefinitionReader成员变量,专门用来读取bean的定义信息

shiro在给securityManager修改cacheManager的时候,就通知了realm。

4.观察者模式

当数据改变时,需要通知其它对象做出相应的动作。并且实现可扩展,松耦合

// 主题对象,包含需要被观察的数据(被观察者) ,此处可以向上抽取被观察者的接口。
class Subject{
    
    Object data;
   	List<Observer> observerList;  // 观察者列表
   
    registerObserver(Observer observer){ // 注册
        observerList.add(observer);
    }
    removeObserver(Observer observer){  // 移除
        int idx = observerList.indexOf(observer);
        if(idx>-1){
            observerList.remove(idx);
        }
    }
    notifyObserver(){ // 通知
        for(observer : observerList){
            observer.update(); // 可选择传递数据过去,或者直接将主题对象直接传过去,这里使用构造方法就传过去了
        }
    }
    void SetData(){
        // ... data数据变化
        notifyObserver();//  数据变化时,通知观察者
    }
}

// 观察者
interface Observer{
   	void update();
}

class ObserverImpl implements Observer{
    Subject subject;
    public ObserverImpl(Subject subject){
        this.subject  = subject; // 将主题对象传给观察者,方便观察者后期移除或添加
    }
    void update(Object args); // 实现接口,给主题对象调用
}

比如:spring容器中的BeanPostProcessor后置处理器就大量的采用了观察者模式;

gui中注册监听事件就是使用的是观察者模式

java中内置了观察者模式:Observable和observer两个类,方便了观察者模式的使用

5.装饰者模式

定义抽象装饰者抽象类继承自Beverage(与目标对象同类型),将目标对象组合到装饰者子类中(可以使用构造方法设置进去)。

然后使用装饰者代替目标对象即可。

特点:把自己的父类放到自己里面作为成员变量来使用。

​ 因为装饰者和其他类都继承了抽象类,那么其实就不好分辨出到底谁是装饰者,那么通过看谁把父类放在成员位置来使用,那么就可以判断出谁是装饰者。

​ 那么一般在设计装饰者的时候,就应该在构造器的时候就要求一定要传入目标对象才行。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w9Qt4CNN-1593279094266)(assets/image-20200627205501837.png)]

abstract class Beverage{ // 抽象接口
    void cost();
}

class HouseHold extends Beverage{ // 具体实现
    void vost(){
        // ...
    }
}

class Decorator extends Beverage{  // 装饰者也实现同一套接口
    Beverage beverage; // 将父类放到成员变量,并将具体实现传入
    public AbstractDecorator(Beverage beverage){
        this.beverage = beverage;
    }
    void cost(){
        // ...前
        beverage.cost();
        // ...后
    }
} 

java.io中的FilterInputStream就是典型的装饰者模式。

还有Reader(抽象父类),FileReader(Reader的子类)BufferedReader(装饰者,同时也是Reader的子类),

BufferedReader可以传入FileReader,来改造FileReader中的方法,让其更加的强大,便于使用,比如读取一行的方法。

分析:为什么装饰者需要继承抽象父类?

如果不继承的话,也能用。但是只能被装饰一次,不能装饰完了之后再被其它装饰类装饰。采用了继承的话,就可以将装饰类对象当作被装饰的目标对象继续装饰下去。可扩展。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值