面向对象与设计原则

三大特性

  • 封装:封装也叫做信息隐匿或者数据访问保护,类通过暴露有限的访问接口,授权外部只能通过有限的方式来访问内部信息或者数据。在java语言中主要是通过public、protected、private等关键字实现的。
  • 继承:是用来表示类之前的is-a关系,比如猫是一种哺乳动物。从继承关系上来讲,继承可以分为单继承和多继承,多继承就是,例如猫既可以是一种哺乳动物,也可以是一种爬行动物。继承最大的优点就是可以实现代码复用,即,可以把子类都具有的属性或者方法提取到父类当中。
  • 多态:子类可以替代父类,类如Map map = new HashMap(),多态提高代码的扩展性和复用性。如下,whatyousay方法中可以根据传入的不同的对象输出不容的内容。

    interface ProgrammingLanguage{
      public void say();
    }
    
    class JavaLanguage implements ProgrammingLanguage{
      public void say(){
        System.out.println("java 是最好的Web语言");
      };
    }
    
    class PhpLanguage implements ProgrammingLanguage{
      public void say(){
        System.out.println("php 是最好的Web语言");
      };
    }
    
    public class Demo{
      public void whatyousay(ProgrammingLanguage language){
        language.say();
      }
      public static void main(String[] args){
        ProgrammingLanguage lan = new JavaLanguage();
        say(lan);
      }
    }

抽象类和接口

抽象类的三个特性(abstract)

  • 抽象类不允许被实例化只能被继承,也就是说你不能new 出来一个抽象类的对象。
  • 抽象类里面有两种属性和方法

    • 抽象属性和方法:只声明,不实现
    • 非抽象属性和方法:声明 + 实现
  • 子类实现抽象方法:子类必须实现父类中的所有抽象方法。

接口的三个属性(interface)

  • 接口不能包含属性
  • 接口只能声明方法,不能包含实现代码(java 8后default可以)
  • 类实现接口类的时候,必须实现接口中声明的所有方法

抽象类和接口的区别

  • 抽象类是对成员变量和方法的抽象,是一种is-a关系,是为了解决代码复用问题

    is-a:代表类与类之间的继承关系
    比如:猫和狗都是动物,都继承动物的特性。
    所谓动物的特性就相当于抽象类中的抽象属性和方法、猫和狗都具有动物的特性,即实现动物的抽象属性
  • 接口仅仅是解决对方法的抽象(抽象类 - 类的抽象),是一种has-a关系,是为了解决解耦问题,提高代码的可扩展性

    has-a:代表对象和它的属性之间的从属关系
    同一个类的对象,通过它的属性的不同值来区别
    比如:小明和小红 都是同一个类(Person) 但是他们有不同的属性值
    Person xiaoming = new XiaoMing("小明")是一个整体和部分的关系

两者再区别

  • 抽象类 - 强调它是什么

    B 继承 抽象类A 并实现了 A 中的抽象属性和方法,那么它就属于 A

    比如 鸟(有翅膀、尖嘴、会下蛋) -继承- B(会说人话):B继承鸟类这个抽象类那么他就得实现鸟类得相关特性,所以它就是一只鹦鹉

  • 接口 - 强调它有什么功能

    Man 和 Woman实现 接口Person 重写 Person 中的相关方法来,但是赋予了不同内容,进而有了不同的能力

总结

  • 抽象类是为了解决代码复用问题

    比如你要写所有的很多鸟相关的类、你把每个鸟都有的属性写在抽象类中、然后再在子类当中写入不同的鸟所特有的属性,这样就不用再在子类中添加这些属性。使得代码高度复用

  • 接口是为了解决抽象问题

    比如说你要写王者荣耀里所有的的英雄的技能,假设这些英雄都只有3个技能,那么我们就在接口中定义(技能一、技能二、技能三)即可、然后每个英雄的技能在子类中具体实现即可。

基于接口而非实现编程

接口意识、封装意识、抽象意识

好的抽象、封装能够提高代码的灵活性,能够更好地应对需求地变化。

如下我们实现一个RabbitMq收发消息的场景,首先,这显然是一种“看似面向对象,实则面向过程”的实现,没有对代码进行有效地抽象,整个发布消息地过程实际上就是,一种代码形式的流水账,另外,一旦需求发生变化后,假如,后期将RabbitMq替换成其它的消息队列,不光是RabbitMqPub类将被废弃,使用到RabbitMqPub的地方也将面临大量的代码修改。

public class RabbitMqPub{
  //用于创建MQ的物理连接
  private static ConnectionFactory factory = new ConnectionFactory();
  static {
     //设置factory的参数 如地址、用户名、密码等
  }
  private Connection conn;
  private Channel ch;
  //获取连接
  public Connection getConnection(){
    if(connnect != null)return conn;
    else return factory.newConnection();
  }
  //获取通道
  public Channel getChannel(Connection conn){
    if(ch != null)return ch;
    else return conn.newChannel();
  }
  //发送消息
  public void publish(Channel ch,String topic,String routeKey,String msg){
    ......
  }
  //还有订阅、连接关闭等方法
  //......
}

public class PublishJob{
  RabbitMqPub pub = new RabbitMqPub();
  public void publishMsg(){
    Connnect conn = pub.getConnection();
    Channel ch = pub.getChannel(conn);
    pub.publish(ch,......);
  }
}

下面是对上有代码的抽象封装并且使用Spring的工厂和反射的思想对MqPub进行配置,在实现多个消息队列的publish后,我们只需要在代码中对相应接口进行调用,而并不需要关心使用的是哪个消息队列,要切换消息队列也仅需在配置文件中进行更改。

public interface MqPub{
  //......
  public void publish(String topic,String routeKey,String msg);
}

public class RabbitMqPub implements MqPub{
  //用于创建MQ的物理连接
  private static ConnectionFactory factory = new ConnectionFactory();
  static {
     //设置factory的参数 如地址、用户名、密码等
  }
  private Connection conn;
  private Channel ch;
  //获取连接
  private Connection getConnection(){
    if(connnect != null)return conn;
    else return factory.newConnection();
  }
  //获取通道
  private Channel getChannel(){
     if(ch != null)return ch;
     else return getConnection().newChannel(); 
  }
  //发送消息
  public void publish(String topic,String routeKey,String msg){
    .....
  }
  //还有订阅、连接关闭等方法
  //......
}

public class PublishJob{
  MqPub pub = MqFactory.getMqPub();
  public void publishMsg(){
    pub.publish(......);
  }
}

public Class MqFactory{
  private final String mqClassPath; 
  
  static{
    String type = getProperties("XXXX");
    if("rabbit".equals(type)){
      mqClassPath = getProperties("XXXX");
    }
  }
  
 public static MqPub getMqPub(){
   Class clz = Class.forName(mqClassPath)
   return clz.newInstance();
 }
}

SOLID原则

  • 单一职责原则(Single Responsibility Principle):一个类或一个模块只负责完成一个职责
  • 开闭原则(Open Close Principle):对拓展开放,对修改关闭,前者是为了应对新的需求,后者是为了保证系统的稳定性
  • 里氏替换原则(Liskov Substitution Principle):子类对象能够替代父类对象出现的任何对象,并保证原来程序的逻辑行为不变及正确性不被改变。
  • 接口隔离原则(Interface Segregation Principle):一个类要给多个客户端使用,那么可以为每个客户端创建一个接口,然后这个类实现所有接口,而不要只实现一个接口,其中包含所有客户端使用的方法。
  • 依赖倒置原则(Dependence Inversion Principle):模块间的依赖通过抽象发生,实现类之间不发生直接的依赖,其依赖是通过抽象类或者接口产生。

你可能感兴趣的:(面向对象与设计原则)