设计模式——七大原则(附代码示例)

一. 设计模式概念

        对接口编程而不是对实现编程;优先使用对象组合而不是继承

二. 设计模式总览 

1. 创建型模式(Creational Patterns):(5)

        单例(Singleton)模式       原型(Prototype)模式        工厂方法(FactoryMethod)模式

        抽象工厂(AbstractFactory)模式         建造者(Builder)模式

2. 结构型模式(Structural Patterns): (7)

        代理(Proxy)模式        适配器(Adapter)模式        桥接(Bridge)模式

        装饰(Decorator)模式        外观(Facade)模式        享元(Flyweight)模式

        组合(Composite)模式        过滤器模式(Filter Pattern)

3. 行为型模式(Behavioral Patterns): (11)

        模板方法(Template Method)模式        策略(Strategy)模式        命令(Command)模式

        职责链(Chain of Responsibility)模式       状态(State)模式        观察者(Observer)模式

        中介者(Mediator)模式        迭代器(Iterator)模式        访问者(Visitor)模式

        备忘录(Memento)模式        解释器(Interpreter)模式 

4. 划分依据

        在编写代码时,其实就是对组件(类、接口等)的一系列操作。所以设计模式可以根据组件的生命周期来划分,在组件定义环节如何构建组件就会用到结构型模型;在组件创建环节使用创建型模式;在组件的使用环节可用行为型模式。当然,每个环节并不是独立的,所以各个环节所应用的模式都应该互相考量。

三. 设计模式七大原则

        设计模式常用的七大原则有:
                   1)单一职责原则       2)接口隔离原则         3)依赖倒转(倒置)原则      4)里氏替换原则
                   5)开闭原则              6)迪米特法则             7)合成复用原则

1. 单一职责原则 

        一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分。每个类只负责自己的事情,而不是变成万能

代码示例

        以交通工具的使用为例:

        方案1,违法单一职责原则

public class SingleResponsibility1 {
    public static void main(String[] args) {
        Vehicle vehicle = new Vehicle();
        vehicle.run("汽车");
        vehicle.run("摩托");
        vehicle.run("飞机");
    }
}
//方案1,违法单一职责原则
class Vehicle{
    public  void run(String vehicle){
        System.out.println(vehicle+"在公路上行驶");
    }
}

        方案2,遵守单一职责原则,但是改动很大,要将类分解,且修改客户端

public class SingleResponsibility2 {
    public static void main(String[] args) {
            RoadVehicle roadVehice = new RoadVehicle();
            roadVehice.run("汽车");
            roadVehice.run("摩托");

            AirVehicle airVehicle = new AirVehicle();
            airVehicle.run("飞机");
    }
}
//方案2,遵守单一职责原则,但是改动很大,要将类分解,且修改客户端
class RoadVehicle{
    public  void run(String vehicle){
        System.out.println(vehicle+"在公路上行驶");
    }
}
class AirVehicle{
    public  void run(String vehicle){
        System.out.println(vehicle+"在天空上行驶");
    }
}

         方案3,没有对原来类对大的修改,只是增加方法,虽然没有在类这个级别上遵守单一职责原则,但在方法级别上仍然遵守单一职责原则

public class SingleResponsibility3 {
    public static void main(String[] args) {
        Vehicle2 vehicle = new Vehicle2();
        vehicle.run("汽车");
        vehicle.run("摩托");
        vehicle.runAir("飞机");
    }
}
//方案3,没有对原来类对大的修改,只是增加方法
// 虽然没有在类这个级别上遵守单一职责原则,但在方法级别上仍然遵守单一职责原则
class Vehicle2{
    public  void run(String vehicle){
        System.out.println(vehicle+"在公路上行驶");
    }
    public  void runAir(String vehicle){
        System.out.println(vehicle+"在天空上行驶");
    }
}

2. 接口隔离原则

        一个类对另一个类的依赖应该建立在最小的接口上。各个类建立自己的专用接口,而不是建立万能接口

代码示例

        实现下列UML类B实现五个方法;类D实现五个方法;类A通过接口Interface1 依赖(使用) B类,但是只会用到1,2,3方法;类C通过接口Interface1 依赖(使用) D类,但是只会用到1,4,5方法

        不足:类A通过接口Interface1依赖类B,类C通过接口Interface1依赖类D,如果接口Interface1对于类A和类C来说不是最小接口,那么类B和类D必须去实现他们不需要的方法。

public class Segregation1 {
    public static void main(String[] args) {
      Interface1 B = new B();
      A a = new A();
      a.depend1(B);
      a.depend2(B);
      a.depend3(B);
    }
}
//接口定义五个方法
interface Interface1{
    void operation1();
    void operation2();
    void operation3();
    void operation4();
    void operation5();
}
//类B实现五个方法
class B implements Interface1{
    @Override
    public void operation1() {
        System.out.println("B实现operation1");
    }
    @Override
    public void operation2() {
        System.out.println("B实现operation2");
    }
    @Override
    public void operation3() {
        System.out.println("B实现operation3");
    }
    @Override
    public void operation4() {
        System.out.println("B实现operation4");
    }
    @Override
    public void operation5() {
        System.out.println("B实现operation5");
    }
}
//类D实现五个接口
class D implements Interface1{
    @Override
    public void operation1() {

    }

    @Override
    public void operation2() {

    }

    @Override
    public void operation3() {

    }

    @Override
    public void operation4() {

    }

    @Override
    public void operation5() {

    }
}
//类A通过接口Interface1 依赖(使用) B类,但是只会用到1,2,3方法
class A{
    public void depend1(Interface1 i){
        i.operation1();
    }
    public void depend2(Interface1 i){
        i.operation2();
    }
    public void depend3(Interface1 i){
        i.operation3();
    }
}
//类C通过接口Interface1 依赖(使用) D类,但是只会用到1,4,5方法
class C{
    public void depend1(Interface1 i){
        i.operation1();
    }
    public void depend4(Interface1 i){
        i.operation4();
    }
    public void depend5(Interface1 i){
        i.operation5();
    }
}

        改进:使用接口隔离原则 

public class SegregationImprove {
   public static void main(String[] args) {
      A a = new A();
      a.depend1(new B());
      a.depend2(new B());
      a.depend3(new B());
    }
}

//接口1
interface  Interface1{
    void operation1();
}
//接口2
interface  Interface2{
    void operation2();
    void operation3();
}
//接口3
interface  Interface3{
    void operation4();
    void operation5();
}

//类B实现
class B implements Interface1,Interface2{
    @Override
    public void operation1() {
        System.out.println("B实现operation1");
    }
    @Override
    public void operation2() {
        System.out.println("B实现operation2");
    }
    @Override
    public void operation3() {
        System.out.println("B实现operation3");
    }
}

//类D实现
class D implements Interface1,Interface3{
    @Override
    public void operation1() {

    }
    @Override
    public void operation4() {

    }
    @Override
    public void operation5() {

    }
}

class A{
    public void depend1(Interface1 i){
        i.operation1();
    }
    public void depend2(Interface2 i){
        i.operation2();
    }
    public void depend3(Interface2 i){
        i.operation3();
    }
}

class C{
    public void depend1(Interface1 i){
        i.operation1();
    }
    public void depend4(Interface3 i){
        i.operation4();
    }
    public void depend5(Interface3 i){
        i.operation5();
    }
}

3. 依赖倒转(倒置)原则

        高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。即面向接口编程,而不是面向实现类

代码示例

        完成Person接受消息功能:方式1,简单比较容易想到,但是如果我们获取的对象是微信等,就要新增类和相应的方法

public class DependenceReversal1 {
    public static void main(String[] args) {
        Person person = new Person();
        person.receive(new Email());
        person.receive02(new Wechat());
    }
}
class Email{
    public String getInfo(){
        return "电子邮件信息:hello";
    }
}
//增加微信
class Wechat{
    public String getInfo(){
        return "微信信息:hello";
    }
}
//方式1,简单比较容易想到,但是如果我们获取的对象是微信等,就要新增类和相应的方法
class Person{
    public void receive(Email email){
        System.out.println(email.getInfo());
    }
    public void receive02(Wechat wechat){
        System.out.println(wechat.getInfo());
    }
}

        改进:引入一个抽象的接口IReceiver,表示接收者,这样Person类与接口IReceiver发生依赖。因为Email,微信等都属于接收者的范围,各自实现IReceiver,符合依赖倒转原则,无需新增方法

public class DependenceReversal {
    public static void main(String[] args) {
        //客户端无需改变
        Person person = new Person();
        person.receive(new Email());
        //新增微信
        person.receive(new Wechat());
    }
}
//定义接口
interface IReceiver{
    public String getInfo();
}
class Email implements IReceiver{
    public String getInfo(){
        return "电子邮件信息:hello";
    }
}
//增加微信
class Wechat implements IReceiver{
    public String getInfo(){
        return "微信信息:hello";
    }
}
//方式2,对接口的依赖
class Person{
    public void receive(IReceiver receiver){
        System.out.println(receiver.getInfo());
    }
}

依赖的3种实现传递的方式

        方式1:通过接口传递实现依赖

public class DependenceReversal {
    public static void main(String[] args) {
        ChangHong changHong = new ChangHong();
        OpenAndClose openAndClose = new OpenAndClose();
        openAndClose.open(changHong);
    }
}
//方式1:通过接口传递实现依赖
// 开关接口
interface IOpenAndClose{
    public void open(ITV itv);
}
//ITV接口
interface ITV{
    public void play();
}
//长虹电视实现
class ChangHong implements ITV{
    @Override
    public void play() {
        System.out.println("打开长虹电视机");
    }
}
//实现接口
class OpenAndClose implements IOpenAndClose{
    @Override
    public void open(ITV itv) {
        itv.play();
    }
}

        方式2:通过构造方法依赖传递

public class DependenceReversal {
    public static void main(String[] args) {
        ChangHong2 changHong2 = new ChangHong2();
        OpenAndClose2 close2 = new OpenAndClose2(changHong2);
        close2.open();
    }
}
//方式2:通过构造方法依赖传递
interface IOpenAndClose2{
    public void open();
}
interface ITV2{
    public void play();
}
//长虹电视实现
class ChangHong2 implements ITV2{
    @Override
    public void play() {
        System.out.println("打开长虹电视机");
    }
}
class OpenAndClose2 implements IOpenAndClose2{
    public ITV2 itv;
    public OpenAndClose2(ITV2 itv){
        this.itv = itv;
    }
    @Override
    public void open() {
        this.itv.play();
    }
}

        方式3:通过setter方法传递 

public class DependenceReversal {
    public static void main(String[] args) {    
        ChangHong3 changHong3 = new ChangHong3();
        OpenAndClose3 close3 = new OpenAndClose3();
        close3.setTv(changHong3);
        close3.open();
    }
}
//方式3:通过setter方法传递
interface IOpenAndClose3{
    public void open();
    public void setTv(ITV3 tv);
}
interface ITV3{
    public void play();
}
//长虹电视实现
class ChangHong3 implements ITV3{
    @Override
    public void play() {
        System.out.println("打开长虹电视机");
    }
}
class OpenAndClose3 implements IOpenAndClose3{
    public ITV3 itv;
    @Override
    public void open() {
        this.itv.play();
    }
    @Override
    public void setTv(ITV3 tv) {
        this.itv = tv;
    }
}

4. 里氏替换原则

        继承必须确保超类所拥有的性质在子类中仍然成立。即继承父类而不去改变父类

代码示例

        B类继承A类,增加一个新功能:两数相加,但是误将A类方法1重写,导致错误

//A类
class A{
    //返回两数之差
    public int func1(int num1,int num2){
        return num1-num2;
    }
}
//B类继承A类,增加一个新功能:两数相加,但是误将A类方法1重写,导致错误
class B extends A{
    public int func1(int a,int b){
        return a+b;
    }
    public int func2(int a,int b){
        return func1(a,b)+9;
    }
}

        改进:创建一个更加基础的基类,把更加基础的方法和成员写在基类中,B类,如果需要使用A类方法,可采用组合方式

//创建一个更加基础的基类
class Base{
    //把更加基础的方法和成员写在基类中
}
//A类
class A extends Base{
    //返回两数之差
    public int func1(int num1,int num2){
        return num1-num2;
    }
}
//B类,如果需要使用A类方法,可采用组合方式
class B extends Base{
    private A al = new A();
    public int func2(int a,int b){
        return al.func1(a,b)+9;
    }
}

5. 开闭原则

        软件实体应当对扩展开放,对修改关闭即扩展新类而不是修改旧类

        (合成复用原则、里氏替换原则相辅相成,都是开闭原则的具体实现规范)

代码示例

        以绘图为例,根据m_type不同调用不同方法绘制不同图形,优点是比较好理解,简单易操作。缺点是违反了设计模式的ocp原则,即对扩展开放,对修改关闭。即当我们给类增加新功能的时候,尽量不修改代码,或者尽可能少修改代码。比如我们这时要新增加一个图形种类,我们需要修改很多地方(创建新类,增加方法,增加判断)。

public class OpenAndClose {
    public static void main(String[] args) {
        GraphicEditor editor = new GraphicEditor();
        editor.drawShape(new Circle());
        editor.drawShape(new Rectangle());
    }
}
class GraphicEditor{
    public void drawShape(Shape s){
        if(s.m_type == 1){
            drawRectangle(s);
        }else {
            drawCircle(s);
        }
    }
    public void drawRectangle(Shape r){
        System.out.println("矩形");
    }
    public void drawCircle(Shape r){
        System.out.println("圆形");
    }
}
class Shape{
    int m_type;
}
class Rectangle extends Shape{
    Rectangle(){
        super.m_type = 1;
    }
}
class Circle extends Shape{
    Circle(){
        super.m_type = 2;
    }
}

        改进:把Shape类做成抽象类,并提供一个抽象的draw方法,让子类去实现,这样我们有新的图形种类时,只需要让新的图形类继承Shape,并实现draw方法即可,使用方的代码就不需要修改,满足了开闭原则。

public class OpenAndClose {
    public static void main(String[] args) {
        GraphicEditor editor = new GraphicEditor();
        editor.drawShape(new Circle());
        editor.drawShape(new Rectangle());
    }
}
class GraphicEditor{
    public void drawShape(Shape s){
        s.draw();
    }
}
abstract class Shape{
    public abstract void draw();
}
class Rectangle extends Shape{
    @Override
    public void draw() {
        System.out.println("绘制矩形");
    }
}
class Circle extends Shape{
    @Override
    public void draw() {
        System.out.println("绘制圆形");
    }
}

6. 迪米特法则 

        最少知识原则,只与你的直接朋友交谈,不跟“陌生人”说话。即无需直接交互的两个类,如果需要交互,使用中间者

        直接朋友: 每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖,关联,组合,聚合等。其中,我们称出现成员变量,方法参数,方法返回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变量的形式出现在类的内部

代码示例

        以学院和学校为例,打印出所有员工。分析SchoolManager直接朋友是Employee,CollegeManager,但是CollegeEmployee不是直接朋友而是以局部变量的方式出现在类内部,违法了迪米特法则

public class Dimilit {
    public static void main(String[] args) {
        SchoolManager schoolManager = new SchoolManager();
        schoolManager.printAllEmployee(new CollegeManager());
    }
}

//学校总部员工类
class Employee{
    private String id;
    public void setId(String id){
        this.id = id;
    }
    public String getId(){
        return id;
    }
}
//学院员工类
class CollegeEmployee{
    private String id;
    public void setId(String id){
        this.id = id;
    }
    public String getId(){
        return id;
    }
}
//管理学院类
class CollegeManager{
    //返回学院员工
    public List<CollegeEmployee> getAllEmployee(){
        List<CollegeEmployee> list = new ArrayList<CollegeEmployee>();
        for (int i = 0; i < 10; i++) {
            CollegeEmployee emp = new CollegeEmployee();
            emp.setId("学院员工id= "+i);
            list.add(emp);
        }
        return list;
    }
}
//管理学校类
class SchoolManager{
    //返回学校总部员工
    public List<Employee> getAllEmployee(){
        List<Employee> list = new ArrayList<Employee>();
        for (int i = 0; i < 5; i++) {
            Employee emp = new Employee();
            emp.setId("学院员工id= "+i);
            list.add(emp);
        }
        return list;
    }
    //打印所有员工
    void printAllEmployee(CollegeManager collegeManager){
        List<CollegeEmployee> list1 = collegeManager.getAllEmployee();
        System.out.println("------------学院员工-------------");
        for (CollegeEmployee collegeEmployee : list1) {
            System.out.println(collegeEmployee.getId());
        }
        List<Employee> list2 = this.getAllEmployee();
        System.out.println("------------总部员工-------------");
        for (Employee employee : list2) {
            System.out.println(employee.getId());
        }
    }
}

改进:将输出学院的员工方法,封装到CollegeManager

public class Dimilit {
    public static void main(String[] args) {
        SchoolManager schoolManager = new SchoolManager();
        schoolManager.printAllEmployee(new CollegeManager());
    }
}

//学校总部员工类
class Employee{
    private String id;
    public void setId(String id){
        this.id = id;
    }
    public String getId(){
        return id;
    }
}

//学院员工类
class CollegeEmployee{
    private String id;
    public void setId(String id){
        this.id = id;
    }
    public String getId(){
        return id;
    }
}

//管理学院类
class CollegeManager{
    //返回学院员工
    public List<CollegeEmployee> getAllEmployee(){
        List<CollegeEmployee> list = new ArrayList<CollegeEmployee>();
        for (int i = 0; i < 10; i++) {
            CollegeEmployee emp = new CollegeEmployee();
            emp.setId("学院员工id= "+i);
            list.add(emp);
        }
        return list;
    }
    //输出学院员工信息
    public void printEmployee(){
        List<CollegeEmployee> list1 = getAllEmployee();
        System.out.println("------------学院员工-------------");
        for (CollegeEmployee collegeEmployee : list1) {
            System.out.println(collegeEmployee.getId());
        }
    }
}

//管理学校类
class SchoolManager{
    //返回学校总部员工
    public List<Employee> getAllEmployee(){
        List<Employee> list = new ArrayList<Employee>();
        for (int i = 0; i < 5; i++) {
            Employee emp = new Employee();
            emp.setId("学院员工id= "+i);
            list.add(emp);
        }
        return list;
    }
    //打印所有员工
    void printAllEmployee(CollegeManager collegeManager){
        //输出学院员工
        collegeManager.printEmployee();
        //输出学校员工
        List<Employee> list2 = this.getAllEmployee();
        System.out.println("------------总部员工-------------");
        for (Employee employee : list2) {
            System.out.println(employee.getId());
        }
    }
}

7. 合成复用原则

        软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。即优先组合,其次继承

        代码示例

        Head类与Person类同生死,组合关系;IDCard类与Person,聚合关系

class Person {
    private IDCard card; //聚合关系
    private Head head = new Head(); //组合关系
}
class IDCard {

}
class Head {

}

四.设计模式核心思想

        1. 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一 起

        2. 针对接口编程,而不是针对实现编程

        3. 为了交互对象之间的松耦合设计而努力 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值