• 设计模式之六大设计原则学习笔记


    六大设计原则学习笔记

    单一职责原则

    应该有且仅有一个原因引起累的变更。
    tips:当有一个类有多个职责可以实现多个接口。

    里氏替换原则

    继承的特点:

    • 代码共享,提高代码的重用性,
    • 提高代码的可扩展性
    • 侵入性(子类必须有父类所以的属性和方法),降低了灵活性
    • 增强了耦合性

    里氏替换原则的定义:

    所有引用基类的地方必须能透明地使用其子类的对象 
    
    • 子类必须完全实现父类的方法
    • 子类可以有自己的个性
    • 覆盖或实现父类的方法时输入参数可以被放大
    • 覆写或实现弗雷德方法时输出结果可以被缩小

    依赖倒置原则

    依赖倒置原则的含义:

    • 高层模块不应该依赖底层模块,二者都应该依赖其抽象
    • 抽象不应该依赖细节
    • 细节应该依赖抽象

    在java中的表现:

    • 模块间的依赖通过抽象发生,实现类之间发生直接的依赖关系,,其依赖关系通过接口或抽象类产生
    • 接口或抽象类不依赖于实现类
    • 实现类依赖接口或抽象类

    例:以人或者司机开汽车为例。

    //司机接口
    public interface IDriver {
    	public void driver(ICar car);
    }
    //司机实现
    public class Driver implements IDriver{
    	@Override
    	public void drive(ICar car) {
    		car.run();
    	}
    }
    //汽车接口
    public interface ICar {
    	public void run();
    }
    //奔驰车实现
    public class Benz implements ICar{
    	@Override
    	public void run() {
    		System.out.println("Benz车正在运行...");
    	}
    }
    //宝马车实现
    public class Bmw implements ICar{
    	@Override
    	public void run() {
    		System.out.println("Bmw车正在运行...");
    	}
    }
    /**
     * 张三开奔驰车
     */
    public class Client1 {
    	public static void main(String[] args) {
    		IDriver ZhangSan = new Driver();
    		ICar benz = new Benz();
    		ZhangSan.drive(benz);
    	}
    }
    /**
     * 张三开宝马车
     */
    public class Client2 {
    	public static void main(String[] args) {
    		IDriver ZhangSan = new Driver();
    		ICar bmw = new Bmw();
    		ZhangSan.drive(bmw);
    	}
    }
    

    从上面的列子我们可以看出,高层模块,如client和driver,不依赖底层模块的,只是依赖接口,如,driver中的car对象只是以ICar声明,通过接口产生;client中也是通过IDriver和ICar来声明对象。

    依赖的三种写法:

    • 构造函数传递依赖对象

        public interface IDriver {
        	public void driver();
        }
        
        public class Driver implements IDriver{
        	private ICar car;
        	//构造函数注入
        	public Driver(ICar _car){
        		this.car = _car
        	}
        	@Override
        	public void driver() {
        		this.car.run();
        	}
        }
      
    • Setter方法传递依赖对象

        public interface IDriver {
        	public void setCar(ICar car);
        	public void driver();
        }
        
        public class Driver implements IDriver{
        	private ICar car;
        	//Setter方法
        	public setCar(ICar _car){
        		this.car = _car
        	}
        	@Override
        	public void driver() {
        		this.car.run();
        	}
        }
      
    • 接口声明依赖对象

        //这段的第一段很长的程序,就是这种情况
        public interface IDriver {
        	public void driver(ICar car);
        }
        public class Driver implements IDriver{
        	@Override
        	public void drive(ICar car) {
        		car.run();
        	}
        }
      

    接口隔离原则

    定义:

    • 客户端不应该依赖它不需要的接口
    • 类间的依赖关系应该建立在最小的接口上

    原则:

    • 接口要尽量小

      这是接口隔离原则的核心定义,不出现臃肿的接口(Fat Interface),但是“小”是有限度的,首先就是不能违反单一职责原则。根据接口隔离原则拆分接口时,首先必须满足单一职责原则。

    • 接口要高内聚

      高内聚就是要提高接口、类、模块的处理能力,减少对外的交互。具体到接口隔离原则就是,要求在接口中尽量少公布public方法,接口是对外的承诺,承诺地越少对系统开发越有利,变更的风险也就越少,同时也有利于降低成本。

    • 定制服务

      定制服务就是单独为一个个体提供优良的服务。

    • 接口设计是有限度的

      接口的设计粒度越小,系统越灵活,这是不争的事实。但是,灵活的同时也带来了结构的复杂化,开发难度增加,可维护性降低,这不是一个项目或产品所期望看到的,所以接口设计一定要注意适度,这个度只能根据经验和常识判断,没有一个固化或可测量的标准。

    迪米特法则LOD(最少知识原则LKP)

    一个对象应该对其他对象有最少的了解
    一个类应该对自己需求耦合或调用的类知道的最少,你(被耦合或调用的类)的内部是多么的复杂都和我没有关系
    低耦合

    开闭原则

    一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。

    在设计一个模块时,应当使这个模块可以在不被修改的前提下被扩展,换言之,应当可以在不必修改源代码的情况下改变这个模块的行为;因为所有软件系统中 有一个共同的特性,即它们的需求都会随时间的推移而发生变化,在软件系统面临新的需 求时,满足开-闭原则的软件中,系统已有模块(特别是最重要的抽象层)不能再修改,而 通过扩展已有的模块(特别是最重要的抽象层),可以提供新的行为,以满足需求。

    开-闭原则如果从另一个角度讲述,就是所谓可变性封装原则(Principle of Encapsulation of Variation,略写作EVP),找到系统的可变因素,将之封装起来。 也就是:考虑你的设计中有什么可能发生变化,允许这些变化而不让这些变化 导致重新设计。可变性封装原则意味着:

    一种可变性不应当散落在代码的很多角落,而应当被封装到一个对象中,同一种可变性 的不同表现可以体现在子类中,继承应当被看做是封装变化的方法,而不仅仅看做是从父类派生子类
    一种可变性不应当与另一种可变性混合在一起,所以一个设计模中,类图的继承层次 不会超过两层,不然就意味着将两种可变性混在一起
    

    做到开闭原则不是件容易的事,但也很多规律可循,这些规律也同样以设计原则的身份 出现,它们都是开-闭原则的手段和工具,是附属于开-闭原则的。
    例:模拟书店销售书籍为例

    public interface IBook {
    	public String getName();
    	public int getPrice();
    	public String getAuthor();
    }
    
    public class NovelBook implements IBook {
    	private String name;
    	private int price;
    	private String author;
    	@Override
    	public String getName() {
    		return this.name;
    	}
    	@Override
    	public int getPrice() {
    		return this.price;
    	}
    	@Override
    	public String getAuthor() {
    		return this.author;
    	}
    	public NovelBook (String _name, int _price, String _author){
    		this.name = _name;
    		this.price = _price;
    		this.author = _author;
    	}
    }
    
    public class BookStore {
    	private final static ArrayList<IBook> booklist = new ArrayList<IBook>();
    	static{
    		booklist.add(new NovelBook("天龙八部",32,"金庸"));
    		booklist.add(new NovelBook("巴黎圣母院",40,"雨果"));
    		booklist.add(new NovelBook("悲惨世界",43,"雨果"));
    	}
    	public static void main(String[] args) {
    		System.out.println("------ 模拟书店卖出去的书记录如下 ------");
    		for (IBook book:booklist) {
    			System.out.print("--- 卖出书:"+book.getName());
    			System.out.print("---价格:"+book.getPrice());
    			System.out.println("---作者:"+book.getAuthor());
    		}
    	}
    }
    

    然而,现在由于需要而需将书打折出售,原价大于40元的9折,原价小于40的八折。

    有三种方法解决这类问题

    • 修改接口
    • 修改实现类(有时候不适用)
    • 通过扩展实现变化

    对于上面的例子,解决办法如下:

    • 对于第一种,可以修改iBook接口,添加getOffPrice()方法
    • 对于第二种,可以修改NovelBook中的getPrice方法
    • 对于第三种,,添加一个OffNovelBook实现iBook接口
  • 相关阅读:
    python
    python
    gitlab
    nodejs
    java
    ElasticSearch 安装与配置 (windows)
    shell脚本批量注释
    C获取系统中CPU核数
    linux内核内存管理
    perf: interrupt took too long (3136 > 3126), lowering kernel.perf_event_max_sample_rate to 63000
  • 原文地址:https://www.cnblogs.com/byonecry/p/4153764.html
Copyright © 2020-2023  润新知