美文网首页
Scala的组合(Composition)和继承(Inherit

Scala的组合(Composition)和继承(Inherit

作者: Grits | 来源:发表于2018-03-13 14:43 被阅读0次

    Key Words: Composition, inheritance, abstract classs, parameterless methods, extending classes, overriding methods and fields, parametric fields, invoking superclass constructors, polymorphism and dynamic binding, final members and classes, factory objects and methods.

    组合和继承

    组合意味着一个类持有另外一个类的引用,来帮助此类来完成任务
    继承是父/子类的关系

    组合和继承是根据已有的类来定义新类的两种方式。当你倾向于代码的重用,你应该选择组合而不是继承。因为继承会导致脆弱基类(fragile base class)问题,很容易破坏子类的实现

    询问自己,继承的模型是否是is a关系,客户端是否想把子类当做超类来使用,如果是这种is a关系,则需要使用继承;如果是has a关系,则使用组合。从设计模式上讲,优先使用组合而非继承

    抽象类(Abstract class)

    使用abstract来修饰一个类,即可将此类申明为抽象类

    abstract class Foo
    

    需要注意的地方:

    1.一个类含有抽象的成员,此类必须要声明为抽象
    2.一个类声明为抽象,则此类可以含有抽象成员
    3.在抽象类中,抽象成员不必使用abstract修饰符

    也就是说,含有抽象成员是声明一个类为抽象类的充分条件

    无参方法(Parameterless methods)

    定义一个方法,该方法没有参数列表(paremeter list),该方法就是无参方法。如果一个方法含有空的圆括号,叫做空参方法(empty-param methods)

    def width: Int // parameterless methods.
    def width() : Int // empty-param methods.
    

    不论是方法没有参数,还是方法访问可变状态(只读)时,都推荐使用无参方法。这个约定支持统一访问原则(uniform access principle)客户端代码不受你决定将一个属性实现为字段还是方法的影响

    总结下来,在Scala中鼓励使用不带参数,当没有副作用的时候,推荐使用无参方法。反过来,你不应该定义有副作用的无参方法,那样会给你的调用方造成很多的困扰。如果你调用一个方法具有副作用,那么也应该带上空括号(Empty parentheses)。

    另外一种思考的方式,如果函数执行操作(performs an operation),则使用圆括号。如果仅仅提供对属性的访问,则去掉圆括号。

    扩展类(Extending class)

    使用extends关键字来对类进行扩展,也就是所谓的继承

    继承(Inheritance)意味着超类的所有成员也会成为子类的成员,但是有两点除外:

    1.超类的私有成员无法继承到子类中
    2.如果子类中有实现和超类一样签名的成员,那么超类的该成员也无法继承到子类中,这也就是所谓的重写(overrides)

    如果超类中的成员是抽象的,在子类中是具体的(concrete),叫做子类实现了超类的抽象成员

    重写方法和字段(Overriding methods and fields)

    统一访问原则的一个方面,就是Scala对待字段和方法更加的统一。在Scala中,字段和方法属于同一个命名空间(就是说在Scala中,禁止在同一个类中出现同名的方法和字段),这就为字段重写为一个无参方法提供了可能

    总的来说,Scala拥有两个命名空间(Java含有四个,字段、方法、类型、包)。

    • 值(字段,方法,包和单例对象)
    • 类型(类和特质)

    所以可以在Scala中,将一个无参方法重写为一个字段

    abstract class Foo {
      def bar: Int
    }
    
    class FooX extends Foo {
      val bar = 1
    }
    

    参数化字段(Parametric fields)

    使用参数化字段可以消灭代码的坏味道(code smell)
    要区别于类参数,其对外不可见

    关于参数化字段的用法:

    1.可以使用var来进行修饰,表示该字段是可以重新赋值的(reassignable)
    2.可以添加修饰符(modifiers),比如 private,protected,override 到参数化字段

    使用参数化字段来重写无参方法时需要注意:该参数化字段不能使用var修饰,只能使用val,同样也不能改变原来方法的可见性(private, protected)

    abstract class Animal {
      def sound: String
    }
    
    class Cat(val sound: String) extends Animal
    
    class Dog(
            private val color: String,
            protected val weight: Int, 
            val sound: String
          ) extends Animal
    
    class Garfield(
            var color: String, 
            override val sound: String
          ) extends Cat(sound)
    
    val cat = new Garfield("Yellow", "Wow")
    
    println(cat.sound)
    println(cat.color)
    

    按照我的理解,类参数,也是参数化字段的一种,只不过是在编译时前面默认添加了private修饰符而已

    重写操作符(Override modifiers)

    在 Scala 中,如果对父类的一个具体方法进行重写,则必须要使用override修饰符,如果是对父类的抽象方法进行重写,则override则是可选的,如果不是重写或者实现父类的成员,那么override修饰符是禁止使用的
    关于重写的上述约定,在系统演进(evolution)过程中非常重要

    当然override也会带来脆弱基类(fragile base class)的问题,就是在基类中添加已经在客户端子类中已有的方法,将会导致编译出错

    多态和动态绑定(Polymorphism and Dynamic binding)

    多态可以从技术上理解为动态绑定。也就是说实际上具体哪个方法实现的调用取决于运行时的对象,而不是变量类型或者表达式

    最终成员(Final members)

    如果想要在继承层次中,使得某个成员不能被子类重写,可以使用final修饰符
    如果想要整个类都不被子类化,可以在类定义上添加final修饰符

    工厂对象(Factory Object)

    工厂对象就是包含那些构建其他对象的对象。客户端可以使用工厂方法来构建对象,而不是直接使用new关键字来进行构建

    使用工厂对象来构建对象会有很多的好处:

    1.将创建过程中心化且隐藏对象的构建细节
    2.为将来提供更多的机会来修改实现代码而不至于破坏客户端代码

    相关文章

      网友评论

          本文标题:Scala的组合(Composition)和继承(Inherit

          本文链接:https://www.haomeiwen.com/subject/ueszfftx.html