精华内容
下载资源
问答
  • C#.NET_面向对象编程技术

    万人学习 2015-01-08 08:56:25
    通俗易懂的面向对象编程技术,详细讲解C#.NET编程原理,.NET框架,辅以编程实例,具体的项目案例。从基础知识到项目开发,由入门到精通。 以形象的描术,把生涩的技术生动形象化,易理解,易掌握。
  • 面向过程编程和面向对象编程的步骤、特性与区别? ** 面向过程编程和面向对象编程的区别 ** 面向过程编程: 分析出解决问题所需要的步骤,然后用函数把这些步骤一步步实现,使用的时候再一个个的依次调用即可。 优点...

    面向过程编程和面向对象编程的步骤、特性与区别?

    **

    面向过程编程和面向对象编程的区别

    **

    面向过程编程:

    分析出解决问题所需要的步骤,然后用函数把这些步骤一步步实现,使用的时候再一个个的依次调用即可。
    优点:性能高
    缺点:相较于面向对象而言,不易维护,不易复用,不易扩展
    适合于小型的项目

    面向对象编程:

    把事务分解成为一个个对象,然后由对象之间分工与合作。
    优点:易维护,易复用,易扩展,低耦合,系统更加灵活
    有三大特性:封装,继承,多态
    缺点:性能比面向过程低
    适合多人协作的大型项目

    用面向过程的方法写出来的程序就像是一份蛋炒饭,先炒蛋,混合些洋葱粒,胡萝卜丝,豌豆,翻炒片刻,再把饭加入锅里,加各种调料,酱油味精什么的,蛋与饭交融在一起,充分混合,加上各种香料,香味已经渗透到饭里面。吃起来让人欲罢不能。但是,对于我这样不喜欢吃蛋的人来说就不是那么友好了。

    用面向对象写出来的程序就像是一份盖浇饭,可以根据不同的人的喜好来烹饪,有的喜欢番茄炒蛋盖浇饭,有的喜欢青椒肉丝盖浇饭,都可以,直接把菜往饭上一堆就可以了,饭菜是相互独立的,由于菜汁是覆盖在饭的上面的,吃的时候还需搅拌搅拌,让菜汁的香味慢慢融合到饭里,没有蛋炒饭来的那么干脆。

    所以蛋炒饭与盖浇饭谁也代替不了谁,它们是为不同的吃货准备的。

    展开全文
  • Scala面向对象编程

    万次阅读 2018-11-07 11:18:49
    Scala面向对象编程 Scala程序设计 第2版 - 原版.pdf 下载: https://download.csdn.net/download/u014646662/10805074 目录: 1 类与对象初步 2 引用与值类型 3 价值类 4 父类 5 Scala的构造器 6 类的字段 6.1...

    Scala面向对象编程

    Scala程序设计 第2版 - 原版.pdf 下载:

    https://download.csdn.net/download/u014646662/10805074

    目录:

    1 类与对象初步
    2 引用与值类型
    3 价值类
    4 父类
    5 Scala的构造器
    6 类的字段
        6.1 统一访问原则
        6.2 一元方法
    7 验证输入
    8 调用父类构造器与良好的面向对象设计
    9 嵌套类型

    对人工智能感兴趣的同学,可以点击以下链接:

    现在人工智能非常火爆,很多朋友都想学,但是一般的教程都是为博硕生准备的,太难看懂了。最近发现了一个非常适合小白入门的教程,不仅通俗易懂而且还很风趣幽默。所以忍不住分享一下给大家。点这里可以跳转到教程。

    https://www.cbedai.net/u014646662

    Scala 是一个函数式编程语言,也是一个面向对象的编程语言,与Java、Python、Ruby、Smalltalk 等其他语言一样。在此强调两点,首先,函数式编程已经成为解决现代编程问题的一项基本技能,这个技能对你而言可能是全新的。开始使用Scala 时,人们很容易把它作为一个“更好的Java”语言来使用,而忽略了它“函数式的一面”。其次,Scala 在架构层面上提倡的方法是:小处用函数式编程,大处用面向对象编程。用函数式实现算法、操作数据,以及规范地管理状态,是减少bug、压缩代码行数和降低项目延期风险的最好方法。另一方面,Scala 的OO 模型提供很多工具,可用来设计可组合、可复用的模块。这对于较大的应用程序是必不可少的。因此,Scala 将两者完美地结合在了一起。

    1 类与对象初步

    类用关键字class 声明,而单例对象用关键字object 声明;(单例模式和Java的实现方式不同)

    在类声明之前加上关键字final,可以避免从一个类中派生出其他类;

    abstract 关键字可以阻止类的实例化;

    一个实例可以使用this 关键字指代它本身。(。尽管在Java 代码中经常看到this 的这种用
    法,Scala 代码中却很少看到。原因之一是,Scala 中没有样板构造函数。)

    Java创建一个Person类:

    package cn.com.tengen.test;
    
    public class Person {
        private String name;
        private int age;
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
        public void setName(String name) { this.name = name; }
        public String getName() { return this.name; }
        public void setAge(int age) { this.age = age; }
        public int getAge() { return this.age; }
    }

    但是如果用Scala实现就比较简单了

    package cn.com.tengen.test
    
    class Person(var name: String, var age: Int){
    
    }
    

    使用scala测试一下

    package cn.com.tengen.test.obj
    
    class Person(var name: String,var age: Int){
    
    }
    
    object Person extends App {
    
        val p = new Person("Lucky",18)
      p.age = 20
        println(p.name+"---"+p.age)
    
    }

    输出结果:

    Lucky---20

    在构造参数前加上var,使得该参数成为类的一个可变字段,这在其他的OO 语言中也称为实例变量或属性。

    在构造参数前加上val,使得该参数成为类的一个不可变字段,

    用case 关键字可以推断出val,同时自动增加一些方法,如下所示:

    package cn.com.tengen.test.obj
    
    case class ImmutablePerson(name: String, age: Int) {
    
    }
    
    object ImmutablePerson extends App{
      val p = new ImmutablePerson("Lucky",18)
      println(p.name+"---"+p.age)
    }

    case做了那些事:

    1.重写了toString
    2.默认实现了equals 和hashCode
    3.默认是可以序列化的,也就是实现了Serializable
    4.自动从scala.Product中继承一些函数
    5.case class构造函数的参数是public级别的,我们可以直接访问
    6.支持模式匹配
    7.实现了copy方法

    method(方法)指与实例绑定在一起的函数。换句话说,它的参数列表中有一个“隐含”的this 参数。方法用关键字def 定义。当其他函数或方法需要一个函数作为参数时,Scala 会自动将可用的方法“提升”为函数,作为前者的函数参数。

    如同大多数静态类型语言一样,Scala 允许方法重载。只要各方法的完整签名是唯一的,两个或更多方法就可以具有相同的名称。方法的签名包括返回类型,方法名称和参数类型的列表(参数的名称不重要)。因此,在JVM 中只凭不同的返回类型不足以区分不同的方法。

      def f1(s : String): Unit ={
        println(s)
      }
    
      def f1(s : String,i : Int): Unit ={
        println(s)
      }

    类型名称必须唯一。

    成员是类的字段、方法或类型的统称。与Java 不同,如果方法有参数列表,该方法可以与类的字段同名:

    2 引用与值类型

    Java 语法为JVM 实现数据的方式提供了模型。首先,它提供了一组原生类型:short、int、long、float、double、boolean、char、byte 和关键字void。它们被存储在堆栈中,或为了获得更好的性能,被存储于CPU 寄存器。

    Scala 固然必须符合JVM 的规则,但Scala 做了改进,使得原生类型和引用类型的区别更明显。

    所有引用类型都是AnyRef 的子类型。AnyRef 是Any 的子类型,而Any 是Scala 类型层次的根类型。所有值类型均为AnyVal 的子类型,AnyVal 也是Any 的子类型。Any 仅有这两个直接的子类型。需要注意,Java 的根类型Object(http://docs.oracle.com/javase/8/docs/api/java/lang/Object.html)实际上更接近Scala 的AnyRef,而不是Any。
    引用类型仍用new 关键字来创建。类似其他不带参数的方法一样,如果构造器不带参数(在有的语言中称为“默认构造器”),我们在使用构造器时也可以去掉后面的括号。


    Scala 沿用了Java 中数字和字符串的字面量语法。例如: 在Scala 中,val name ="Programming Scala" 与val name = new String("Programming Scala") 等价。不过,Scala还为元组增加了字面量语法,(1,2,3) 就等价于new Tuple3(1,2,3)。我们已经接触过Scala的一些语言特性,可以实现编译器原本不支持的字面量写法,如:用1 :: 2 :: 3 :: Nil表示Map("one" ->, "two" -> 2)。
    用带apply 方法的对象创建引用类型的实例是很常见的做法,apply 方法起到工厂的作用(这种方法必须在内部调用new 或对应的字面量语法)。由于case 类会自动生成伴随对象及其apply 方法,因此case 类的实例通常就是用这种方法创建出来的。
    Short、Int、Long、Float、Double、Boolean、Char、Byte 和Unit 类型称为值类型,分别对应JVM 的原型short、int、long、float、double、boolean、char、byte 和void 关键字。在Scala 的对象模型中,所有的值类型均为AnyVal 的子类型,AnyVal 是Any 的两个子类型之一。
    值类型的“实例”不是在堆上创建的。相反,Scala 用JVM 原生类型来表示值类型,它们的值都存放在寄存器或栈上。值类型的“实例”总是用字面量来创建,如1,3.14,true。Unit 对应的字面量是(),不过我们很少显式地使用。
    事实上,值类型没有构造器,所以像val I = new Int(1) 这样的表达式将无法通过编译。

    3 价值类

    package cn.com.tengen.test.obj
    
    class Dollar(val value: Float) extends AnyVal {
      override def toString = "$%.2f".format(value)
    }
    object Dollar extends App{
      val hello = new Dollar(100)
      println(hello)
    }
    
    
    //输出结果:$100.00

    要成为一个有效的价值类,必须遵守以下的规则。

    (1) 价值类有且只有一个公开的val 参数(对于Scala 2.11,该参数也可以是不公开的)。
    (2) 参数的类型不能是价值类本身。
    (3) 价值类被参数化时,不能使用@specialized(http://www.scala-lang.org/api/current/scala/specialized.html)标记。
    (4) 价值类没有定义其他构造器。
    (5) 价值类只定义了一些方法,没有定义其他的val 和var 变量。
    (6) 然而,价值类不能重载equals 和hashCode 方法。
    (7) 价值类定义没有嵌套的特征、类或对象。
    (8) 价值类不能被继承。
    (9) 价值类只能继承自通用特征。
    (10) 价值类必须是对象可引用的一个顶级类型或对象的一个成员。

    通常,被包装的类型是AnyVal 的子类型之一,但并不是必须如此。如果换成引用类型,我们仍然可以受益于内存不在堆上分配的优势。例如,下例中,隐含了对电话号码字符串的包装:

    package cn.com.tengen.test.obj
    
    class USPhoneNumber(val s: String) extends AnyVal {
      override def toString = {
        val digs = digits(s)
        val areaCode = digs.substring(0,3)
        val exchange = digs.substring(3,6)
        val subnumber = digs.substring(6,10) // “客户编号”
        s"($areaCode) $exchange-$subnumber"
      }
      private def digits(str: String): String = str.replaceAll("""\D""", "")
    }
    
    object USPhoneNumber extends App{
      val number = new USPhoneNumber("987-654-3210")
      println(number)
    }
    
    //输出结果:(987) 654-3210
    

    一个通用特征具有以下特性:

    (1) 它可以从Any 派生(而不能从其他通用特征派生)。
    (2) 它只定义方法。
    (3) 它没有对自身做初始化。

    下面给出了一个改进版的USPhoneNumber,这里混用了两个通用特征:

    package cn.com.tengen.test.obj
    
    /**
      * Digitizer 是一个通用特征,定义了我们之前的digits 方法。
      */
    trait Digitizer extends Any {
      def digits(s: String): String = s.replaceAll("""\D""", "")
    }
    
    /**
      * Formatter 特征按我们想要的格式对电话号码进行格式化。
      */
    trait Formatter extends Any {
      def format(areaCode: String, exchange: String, subnumber: String): String =
        s"($areaCode) $exchange-$subnumber"
    }
    
    
    class USPhoneNumber(val s: String) extends AnyVal
      with Digitizer with Formatter {
      override def toString = {
        val digs = digits(s)
        val areaCode = digs.substring(0,3)
        val exchange = digs.substring(3,6)
        val subnumber = digs.substring(6,10)
        //调用Formatter.format。
        format(areaCode, exchange, subnumber)
      }
    }
    object USPhoneNumber extends App{
      val number = new USPhoneNumber("987-654-3210")
      println(number)
    }
    //输出结果:(987) 654-3210
    

    Formatter 实际上解决了一个设计上的问题。我们可能要给USPhoneNumber 指定另一个参数作为格式字符串,或需要一些机制去配置toString 的格式,因为流行的格式可能有很多。但是,我们只允许传递一个参数给USPhoneNumber。针对这种情况,我们可以在通用特征中去配置我们想要的格式!然而,由于JVM 的限制,通用特征有时会触发实例化(即实例的内存分配于堆中)。这里将需要实例化的情况总结如下。

    (1) 当价值类的实例被传递给函数作参数,而该函数预期参数为通用特征且需要被实例实现。不过,如果函数的预期参数是价值类本身,则不需要实例化。
    (2) 价值类的实例被赋值给数组。
    (3) 价值类的类型被用作类型参数。

    4 父类

    子类是从父类或基类中派生的派生类,是大部分面向对象语言的核心特征。这种机制用来重用、封装和实现多态行为(具体行为取决于实例在类型层次结构中的实际类型)。像Java 一样,Scala 只支持单一继承,而不是多重继承。子类(或派生类)可以有且只有一个父类(即基类)。唯一的例外是,Scala 的类型结构中的根类Any 没有父类。

    实例:

    abstract class BulkReader {
      type In
      val source: In
      def read: String // Read source and return a String
    }
    class StringBulkReader(val source: String) extends BulkReader {
      type In = String
      def read: String = source
    }
    class FileBulkReader(val source: java.io.File) extends BulkReader {
      type In = java.io.File
      def read: String = {...}
    }

    如在Java 中一样,关键字extend 表示后面是父类,因此本例中的父类为BulkReader。
    在Scala 中,当类继承trait 时,也用extend 表示(甚至当该类用with 关键字混入了其他trait时也是如此)。
    此外,当trait 是其他trait 或类的子trait 时,也用extend。是的,trait 可以继承类。
    如果我们不指定父类,默认父类为AnyRef。
     

    5 Scala的构造器

    Scala 将主构造器与零个或多个辅助构造器区分开,辅助构造器也被称为次级构造器。在Scala 中,主构造器是整个类体。构造器所需的所有参数都被罗列在类名称后面。

    package cn.com.tengen.test.obj
    
    case class Address(street: String, city: String, state: String, zip: String) {
      def this(zip: String) = this("[unknown]", Address.zipToCity(zip), Address.zipToState(zip), zip)
    }
    object Address {
      def zipToCity(zip: String) = "Anytown"
      def zipToState(zip: String) = "CA"
    }
    case class Person(name: String, age: Option[Int], address: Option[Address]) {
      def this(name: String) = this(name, None, None)
      def this(name: String, age: Int) = this(name, Some(age), None)
      def this(name: String, age: Int, address: Address) = this(name, Some(age), Some(address))
      def this(name: String, address: Address) = this(name, None, Some(address))
    }
    

    构造器调用

    object Main extends App{
      var address = new Address("Lucky");
      var p = Person("lucky",Some(20),Some(address));
      println(p.name+"---"+p.age.get+"---"+p.address.get)
    }

    在我们的实现中,用户使用 new 来创建实例,使用主构造器创建实例时new可以省略

    6 类的字段

    如果在主构造函数参数的前面加上val 或var 关键字,该参数就成为实例的一个字段。对于case 类,val 是默认的。这样可以大大减少冗余的代码。是Scala 会自动做Java 代码中明显做的事情。类会创建一个私有的字段,并生产对应的getter 和setter 访问方法。

    class Name (var value: String)

    改代码等价于

    class Name(s: String){
      private var _value: String = s //不可见的字段,在本例中声明为可变变量。
      def value: String = _value //getter,即读方法。
      def value_= (newValue: String): Unit = _value = newValue //setter,即写方法。
    }

    注意value_= 这个方法名的一般规范。当编译器看到这样的一个方法名时,它会允许客户端代码去掉下划线_,转而使用中缀表达式,这就好像我们是在设置对象的一个裸字段一样:

    object Main extends App{
      val name = new Name("Lucky")
      println(name.value)
      name.value_=("Helen")
      println(name.value)
      name.value="Jon"
      println(name.value)
      name.value=("Lucy")
      println(name.value)
    }
    
    运行结果:
    Lucky
    Helen
    Jon
    Lucy

    6.1 统一访问原则

    Scala 没有遵循JavaBeans 的约定规范,没有把value 字段的读方法和写方法分别命名为getValue 和setValue,但是Scala 遵循统一访问的原则。正如我们在Name 这个例子中看到的,客户端似乎可以不经过方法就对“裸”字段值进行读和写的操作,但实际上它们调用了方法。

    请注意,用户的“体验”是一致的。用户代码不了解实现,这使我们可以在需要的时候,自由地将直接操作裸字段改为通过访问方法来操作。例如:我们要在写操作中添加某些验证工作,或者为了提高效率,在读取某个结果时采用惰性求值。这些情况下,我们可以通过访问方法来操作裸字段。相反地,我们也可以用公开可见性的字段代替访问方法,以消除该方法调用的开销(尽管JVM 可能会消除这种开销)。因此,统一访问原则的重要益处在于,它能最大程度地减少客户端代码对类的内部实现所必须了解的知识。尽管重新编译仍然是必需的,我们可以改变具体实现,而不必强制客户端代码跟着做出改变。

    Scala 实现统一访问原则的同时,没有牺牲访问控制功能,并且满足了在简单的读写基础上增加其他逻辑的需求。

    6.2 一元方法

    package cn.com.tengen.test.obj
    
    case class Complex(real: Double, imag: Double) {
      //方法名为unary_X,这里X 就是我们想要使用的操作符。在本例中,X 就是-。注意-和: 之间的空格,空格在这里是必须的,它可以告诉编译器方法名以- 而不是: 结尾!为了比较,我们也实现了常见的减号操作符。
      def unary_- : Complex = Complex(-real, imag)
      def -(other: Complex) = Complex(real - other.real, imag - other.imag)
    }
    
    object Main extends App{
      val c1 = Complex(88.8, 88.8)
      val c2 = -c1
      val c3 = c1.unary_-
      val c4 = c1 - Complex(22.2, 22.2)
      println(c1)
      println(c2)
      println(c3)
      println(c4)
    }
    
    输出结果:
    Complex(88.8,88.8)
    Complex(-88.8,88.8)
    Complex(-88.8,88.8)
    Complex(66.6,66.6)
    

    方法名为unary_X,这里X 就是我们想要使用的操作符。在本例中,X 就是-。

    注意-和: 之间的空格,空格在这里是必须的,它可以告诉编译器方法名以- 而不是: 结尾!

    我们一旦定义了一元操作符,就可以将操作符放在实例之前,就像我们在定义c2 时所做的那样。也可以像定义c3 时那样,将一元操作符当做其他方法一般进行调用。

    7 验证输入

    保证创建实例参数处于有效的状态,示例如下:

    package cn.com.tengen.test.obj
    
    case class ZipCode(zip: Int, extension: Option[Int] = None) {
      // 使用require 方法验证输入。
      require(valid(zip, extension),  s"Invalid Zip+4 specified: $toString")
      protected def valid(z: Int, e: Option[Int]): Boolean = {
        if (0 < z && z <= 99999) e match {
          case None => validUSPS(z, 0)
          case Some(e) => 0 < e && e <= 9999 && validUSPS(z, e)
        }
        else false
      }
    
      /**
        * 真正的方法实现应该查询USPS 认可的数据库来验证邮政编码是否确实存在。
        */
      protected def validUSPS(i: Int, e: Int): Boolean = true
    
      /**
        * 覆写toString 方法,返回符合人们预期的邮政编码格式,对结尾可能的四位数字进行覆写toString 方法,
        * @return 返回符合人们预期的邮政编码格式,对结尾可能的四位数字进行
        */
      override def toString =  if (extension != None) s"$zip-${extension.get}" else zip.toString
    }
    object ZipCode {
      def apply (zip: Int, extension: Int): ZipCode =
        new ZipCode(zip, Some(extension))
    
      def main(args: Array[String]): Unit = {
        var z1 = ZipCode(12345)
        println(z1) //12345
        var z2 = ZipCode(12345, Some(6789))
        println(z2) //12345-6789
        var z3 = ZipCode(123456)
        println(z3) //异常 java.lang.IllegalArgumentException: requirement failed: Invalid Zip+4 specified: 123456
      }
    }
    

    定义ZipCode 这种领域专用的类的充分理由是:这种类可以在构造时对值的有效性做一次 检验,然后类ZipCode 的使用者就不再需要再次检验了。

    虽然我们在构造器的背景下讨论输入的验证,但实际上我们也可以在任何方法中调用这些断言方法。然而,价值类的类体是一个例外,它不能使用断言验证,否则就需要调用分配堆。不过,由于ZipCode 带有两个构造器参数,它无论如何不会是价值类。

    8 调用父类构造器与良好的面向对象设计

    派生类的主构造器必须调用父类的构造器,可以是父类的主构造器或次级构造器。

    package cn.com.tengen.test.obj
    
    case class Address(street: String, city: String, state: String, zip: String) {
      def this(zip: String) = this("[unknown]", Address.zipToCity(zip), Address.zipToState(zip), zip)
    }
    
    object Address {
      def zipToCity(zip: String) = "Jiaxing"
      def zipToState(zip: String) = "CA"
    }
    
    case class Person( name: String, age: Option[Int] = None, address: Option[Address] = None)
    
    class Employee( name: String, age: Option[Int] = None,  address: Option[Address] = None, val title: String = "[unknown]",  val manager: Option[Employee] = None) 
      extends Person(name, age, address) {
      override def toString = s"Employee($name, $age, $address, $title, $manager)"
    }
    
    object Employee extends App {
      val a1 = new Address("1 Scala Lane", "Anytown", "CA", "98765")
      val a2 = new Address("98765")
      val ceo1 = new Employee("Joe CEO", title = "CEO")
      println(ceo1) //Employee(Joe CEO, None, None, CEO, None)
      val ceo2 = new Employee("Buck Trends1");
      println(ceo2) //Employee(Buck Trends1, None, None, [unknown], None)
    }

    在Java 中,我们会定义构造方法,并用super 调用父类的初始化逻辑。而Scala 中,我们用ChildClass(…) extends ParentClass(…) 的语法隐式地调用父类的构造器。

    当使用继承时,建议遵循以下规则。

    (1) 一个抽象的基类或trait,只被下一层的具体的类继承,包括case 类。
    (2) 具体类的子类永远不会再次被继承,除了两种情况:
    	a. 类中混入了定义于trait中的其他行为。理想情况下, 这些行为应该是正交的, 即不重叠的。
    	b. 只用于支持自动化单元测试的类。
    (3) 当使用子类继承似乎是正确的做法时,考虑将行为分离到trait 中,然后在类里混入这些trait。
    (4) 切勿将逻辑状态跨越父类和子类。

    换一种实现方式:

    Employee 不再是Person 的一个子类,但它是PersonState 的一个子类,因为它混入了该trait。

    package cn.com.tengen.test.obj
    
    case class Address(street: String, city: String, state: String, zip: String)
    
    object Address {
      def apply(zip: String) = new Address("[unknown]", Address.zipToCity(zip), Address.zipToState(zip), zip)
      def zipToCity(zip: String) = "Anytown"
      def zipToState(zip: String) = "CA"
    }
    
    trait PersonState {
      val name: String
      val age: Option[Int]
      val address: Option[Address]
    }
    
    case class Person(name: String,age: Option[Int] = None,address: Option[Address] = None)
      extends PersonState
    
    trait EmployeeState {
      val title: String
      val manager: Option[Employee]
    }
    case class Employee(name: String,age: Option[Int] = None, address: Option[Address] = None, title: String = "[unknown]", manager: Option[Employee] = None)
      extends PersonState with EmployeeState
    
    object Person extends App{
      val ceoAddress = Address("1 Scala Lane", "Anytown", "CA", "98765")
      println(ceoAddress)
      val buckAddress = Address("98765")
      println(buckAddress)
      val ceo = Employee( name = "Joe CEO", title = "CEO", age = Some(50), address = Some(ceoAddress), manager = None)
      println(ceo)
      val ceoSpouse = Person("Jane Smith", address = Some(ceoAddress))
      println(ceoSpouse)
      val buck = Employee(  name = "Buck Trends", title = "Zombie Dev", age = Some(20), address = Some(buckAddress), manager = Some(ceo))
      println(buck)
      val buckSpouse = Person("Ann Collins", address = Some(buckAddress))
      println(buckSpouse)
    }
    
    
    输出结果:
    Address(1 Scala Lane,Anytown,CA,98765)
    Address([unknown],Anytown,CA,98765)
    Employee(Joe CEO,Some(50),Some(Address(1 Scala Lane,Anytown,CA,98765)),CEO,None)
    Person(Jane Smith,None,Some(Address(1 Scala Lane,Anytown,CA,98765)))
    Employee(Buck Trends,Some(20),Some(Address([unknown],Anytown,CA,98765)),Zombie Dev,Some(Employee(Joe CEO,Some(50),Some(Address(1 Scala Lane,Anytown,CA,98765)),CEO,None)))
    Person(Ann Collins,None,Some(Address([unknown],Anytown,CA,98765)))
    

    9 嵌套类型

    Scala 允许我们嵌套类型的成名和定义。例如:在对象中定义类型转义的异常和其他有用的类型,就是很常见的做法。以下是一个数据库层可能的骨架结构:

    package cn.com.tengen.test.obj
    
    object Database {
      case class ResultSet(/*...*/)
      case class Connection(/*...*/)
      case class DatabaseException(message: String, cause: Throwable) extends RuntimeException(message, cause)
    
      /**
        * 使用sealed 的继承结构表示状态;所有允许的值都在这里定义。当实例实际上不携带状态信息时,使用case 对象。这些对象表现得像“标志位”,用来表示状态。
        */
      sealed trait Status
      case object Disconnected extends Status
      case class Connected(connection: Connection) extends Status
      case class QuerySucceeded(results: ResultSet) extends Status
      case class QueryFailed(e: DatabaseException) extends Status
    }
    class Database {
      import Database._
    
      /**
        * ??? 是定义在Predef 中的真实方法。它会抛出一个异常,用来表示某一方法尚未实现的情况。该方法是最近才引入Scala 库的。
        */
      def connect(server: String): Status = ???
      def disconnect(): Status = ???
      def query(/*...*/): Status = ???
    }

    当case 类没有用任何字段表示状态信息时,考虑使用case 对象。

    当方法还正处在开发阶段时,??? 方法作为占位符十分有用。因为这样可以使代码通过编译。问题是你没法调用那个方法!

    为case 对象生成的hashCode 方法仅仅将对象的名称做了散列。

     def main(args: Array[String]): Unit = {
        println(Disconnected.hashCode())
        println("Disconnected".hashCode())
      }
    
    输出结果:
    -1217068453
    -1217068453

     

    展开全文
  • 现在网络上还经常讨论函数式编程和面向对象编程。很多人认为,C语言编程,就是函数式编程,C++, JAVA等高级语言,才是面向对象编程。 其实,这是错误的认识!面向对象编程是一种思想,是一种编程思路,实现这种...

           结构体是一个非常重要的知识点,必须掌握和深入理解使用。现在网络上还经常讨论函数式编程和面向对象编程。很多人认为,C语言编程,就是函数式编程,C++, JAVA等高级语言,才是面向对象编程。

           其实,这是错误的认识!面向对象编程是一种思想,是一种编程思路,实现这种编程思路,不局限于是使用C语言还是使用C++语言。

           使用C语言也可以实现面向对象编程。例如linux的内核,这么复杂的一个操作系统,是使用C语言来设计完成,很多地方是使用了面向对象的编程思想。

           那么,使用C语言来实现“面向对象编程”思路,就是使用结构体来完成。C++中使用类来定义成员变量和方法,相应的,C语言也可以使用结构体来完成这项操作。C语言的结构体可以定义成员变量,同时,可以定义函数指针,实现C++类的方法。

           下面我通过程序测试例子来讲解“使用结构体实现面向对象编程”的知识。首先,给出程序测试例子代码:

           程序运行结果如下:

           在这个例子中,讲解一个图书管理系统中的借书和还书功能。对于读者来说,有普通读者和VIP读者。读者有借书和还书的操作。

           此时,我们使用面向对象编程的思想来设计程序。首先,分析设计程序要实现什么功能,它有什么对象进行活动,对象之间是什么关系,对象有什么操作。那么,我们可以归纳如下:

    (1) 程序是一个图书馆的借书和还书功能模块;

    (2) 程序是涉及到的活动对象是读者和图书;

    (3) 对象之间的活动关系是:读者结束、读者还书;

    (4) 读者有普通用户和VIP用户类型的区别;

    (5) 普通用户和VIP用户的借书和还书有普通的操作;

           所以,归纳出了这些问题,我们就可以有如下是设计思路:

    (1) 需要定义读者和图书这样的两个结构体类型;所以,我们定义了struct reader, struct book这样的结构体类型;

    (2) 读者结构体类型中,需要定义普通用户和VIP用户的区分属性;所以,在struct reader结构体类型中,定义了 is_vip属性,用于定义当前读者的身份类型;

    (3) 普通读者和VIP读者的借书和还书操作不一样,所以,定义普通读者操作是:

    void my_borrow_book(struct reader* preader, struct book* pbook)

    void my_payback_book(struct reader* preader, struct book* pbook)

    VIP读者的操作是:

    void my_vip_borrow_book(struct reader* preader, struct book* pbook)

    void my_vip_payback_book(struct reader* preader, struct book* pbook)

           这些函数操作是读者的一个属性,所以,需要给读者定义函数指针成员,指向具体的操作函数。如下:

    //定义函数指针, pfunc_borrow_book 类型的函数;

    pfunc_borrow_book borrow_book;

    //定义函数指针, pfunc_payback_book 类型的函数;

    pfunc_payback_book payback_book;

           最终,在init_reader_function()函数中,根据读者是否为VIP类型,给对象的函数指针成员指向不同的函数。如下:

        if(preader->is_vip)

        {

           //给函数指针赋值, 让它指向具体的函数;

           preader->borrow_book = my_vip_borrow_book;

           preader->payback_book = my_vip_payback_book;

        }

        else

        {

           preader->borrow_book = my_borrow_book;

           preader->payback_book = my_payback_book;

        }

           那么,不同的读者类型,调用相同的函数指针,就调用指向不同的函数。例如:

    r1.borrow_book(&r1, &b1);   //读者 r1 借书

    r2.borrow_book(&r2, &b2);   //读者 r2 借书

           此时,读者都是调用borrow_book函数指针指向的函数,那么,由于r1是普通用户,它的函数指针指向my_borrow_book()函数,所以,r1.borrow_book()调用的就是my_borrow_book()函数。

           同理,r2是VIP用户,它的borrow_book函数指针指向my_vip_borrow_book ()函数,所以,r2.borrow_book()调用的就是my_vip_borrow_book ()函数。

           通过这个例子,我们更深入地学习了结构体的应用,并提出了面向对象的编程思想。使用C语言的结构体知识可以实现面向对象编程。

    更多的交流可以访问:www.mylinux.vip   加QQ:1523520001,微信:13926572996,备注:linux编程;
    学习、分享更多的linux C/C++ 编程知识。
     

     

    展开全文
  • 面向对象编程

    千次阅读 2006-08-27 21:10:00
    1.面向对象编程需要注意的三点:1>组成优于继承2>对接口编成。父类的引用指向子类的实例,这样可以通过父类的调用得到子类的最新方法3>适应变化,需要具有灵活性2.面向对象的三大特性:1>封装封装的对象:数据(私有...

    1.面向对象编程需要注意的三点:
    1>组成优于继承
    2>对接口编成。父类的引用指向子类的实例,这样可以通过父类的调用得到子类的最新方法
    3>适应变化,需要具有灵活性

    2.面向对象的三大特性:
    1>封装
    封装的对象:数据(私有化,保证它的隐藏性)、特有的构造方法、业务方法、辅助方法(供内部使用的辅助方法,可以单提出为辅助类)
    需要保证的特性:稳定性(接口稳定,接口中方法的参数可以使用Object)
    2>继承(原有代码的扩充和变更)
    特点:
    ●子类具有父类的特征
    ●父类的扩充
    与组成的区别:
    ●继承得到更多父类成员,可以直接使用,属于紧耦合
    ●组成则需要先实例化,但提供更好的动态处理,属于松耦合。实例化时可以存在多种实例化方式,如下代码:
    private Product product;
    public UseTest(FoodProduct food){
     this.product=food;
    }
    3>多态

    3.类的使用:(高内聚,低耦合,功能单一)
    具体类:尽量避免做父类
    抽象类(父类):有缺省实现时使用,且缺省实现用final来避免再实现
    接口(父类):充分利用它子类多态性的特点,让实现灵活可变

    4.类之间的关系:
    层次关系:继承(父子关系)
    成员关系:关联关系
                        聚合关系(整体和部分) |
                        组成关系                               |二者区别:整体和部分是否同生同灭
    参数关系:以参数形式进入某一方法

    5.面向对象编程的原理
    1>OCP开放封闭原理(Open-Closed Principle)
      开放便于扩展,封闭利于修改
    2>LSP替代原理(Liskov Substitute Principle)
      子类可以替代其父类
    3>DIP依赖倒置原理(Dependency Inversion Principle)
      依赖于抽象类,不依赖于具体类
    4>ISP接口分离原理(Interface Segregation Principle)
      多个专用接口优于一个单一的通用接口
    5>CRP组成重用原理(Composite Reuse Principle)
      对象组成部分的多态优于继承
    6>PLK最少知识原理(Principle of Least Knowledge)
      在类的方法中,只调用本身、参数对象、局部对象以及类的实例对象的操作。尽可能用最少的对象获取所需方法 

    展开全文
  • Java 面向对象编程.pdf

    千次下载 热门讨论 2012-12-24 17:41:29
    《Java 面向对象编程》内容由浅入深,紧密结合实际,利用大量典型实例,详细讲解Java面向对象的编程思想、编程语法和设计模式,介绍常见Java类库的用法,总结优化Java编程的各种宝贵经验,深入阐述Java虚拟机执行...
  • JavaScript ES6面向对象编程

    千人学习 2018-05-11 17:38:33
    JavaScript中的面向对象编程是个难点。那这个课程会非常详细去介绍面向对象,其中晦涩的概念都会用非常通俗的语言去阐述!课程里面还包括ES6的面向对象,通过这套课程会让你彻底弄明白面向对象!
  • 面向接口编程和面向对象编程的区别

    万次阅读 多人点赞 2017-11-02 11:21:40
    我想,对于各位使用面向对象编程语言的程序员来说,“接口”这个名词一定不陌生,但是不知各位有没有这样的疑惑:接口有什么用途?它和抽象类有什么区别?能不能用抽象类代替接口呢?而且,作为程序员,一定经常听到...
  • 面向对象编程简单介绍

    千次阅读 2021-03-18 21:36:54
    面向对象编程介绍面向对象编程介绍1 面向对象编程 oop ( object Orinted programming)面向对象的三个特征2 类和对象对象类 class类 constructor 构造函数3 类的继承类继承 extends和super关键字super调用父类普通...
  • 面向过程编程,面向对象编程和面向切面编程理解

    千次阅读 多人点赞 2017-03-17 19:28:55
    面向过程(Procedure ...面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)是一种计算机编程架构。OOP 的一条基本原则是计算机程序是由单个能够起到子程序作用的单元或对象组合而成。OOP 达到
  • Go:面向对象编程

    万次阅读 2020-06-01 09:49:21
    Go语言面向对象编程
  • C 语言实现面向对象编程

    万次阅读 多人点赞 2018-08-14 18:36:38
    C 语言实现面向对象编程 1、引言 面向对象编程(OOP)并不是一种特定的语言或者工具,它只是一种设计方法、设计思想。它表现出来的三个最基本的特性就是封装、继承与多态。很多面向对象的编程语言已经包含这三...
  • 什么是面向对象编程

    千次阅读 多人点赞 2020-01-12 18:57:20
    本文关键字:面向对象、面向过程、面向过程编程语言、面向对象编程语言。说到编程,对于初学者来讲,可能第一想到的就是敲键盘,写代码,做游戏,甚至于会联想到软件破解、网络攻防。另一方面,在学了一些编程的相关...
  • js面向对象编程

    千次阅读 多人点赞 2020-02-23 22:07:02
    面向对象编程其实是一种思想(面向过程,面向对象) 面向过程: 比如说你现在肚子饿了,想要去吃面条,那么在做面条的过程中用多少面粉,用多少水,怎么和面和切面条 ...
  • 面向对象编程转为面向接口编程

    千次阅读 2018-08-19 01:48:07
    但是,如果只是停留在面向对象编程,而不是继续向前看。那么,无疑是一种固步自封的陷阱。 更加先进的编程思想是什么呢? 就是面向接口编程。   这里,涉及到一个哲学问题。到底是以类型封装特征,还是以特征...
  • JS深度揭秘第五章-面向对象编程

    千人学习 2018-11-14 17:59:40
    此章节会详细介绍对象的概念,包含javascript的内置对象、ES6增加的内置对象等。会着重的讲解面向对象编程,通俗的解释...用独特的方式去讲解面向对象编程的方式以及ES6的面向对象编程,可以很轻松的理解面向对象编程
  • 首先java就是面向对象编程,所谓在java中万事万物皆对象,这是因为java中所有类的调用都是new出来的, 其次面向对象用到三大特性,即封装、多态、继承。 2、面向接口编程: 一个接口可以从三方面去考察:制定...
  • 简述面向函数编程和面向对象编程的区别? 什么时候使用面向函数编程?什么时候使用面向对象编程? 函数式编程,顾名思义,这种编程是以函数思维做为核心,在这种思维的角度去思考问题。 这种编程最重要的基础是λ...
  • 面向对象编程以及Python面向对象

    千次阅读 2015-11-04 22:59:14
    一、面向对象编程 面向对象编程(OOP)作为一种方式,相对于面向过程更加的贴近人的思维习惯。 面向对象是将一个整体问题划分为若干个模块,通过块与快之间的交流来解决问题;而面向过程编程是将一个问题按照...
  • JavaScript面向对象编程指南.pdf

    千次下载 热门讨论 2015-12-10 16:06:49
    JavaScript面向对象编程指南.pdf
  • C++-面向对象编程-000-面向对象

    千次阅读 2020-03-16 19:14:28
    C+±面向对象编程-000-2020-3-16
  • 面向对象编程和泛型编程

    千次阅读 2018-08-02 11:10:48
    C++不仅支持面向对象编程而且还支持泛型编程,那么我们是否在大脑中清晰的知道什么是面向对象编程和泛型编程吗?那面向过程编程了?? (1).面向对象编程简称OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,...
  • 面向过程编程 面向过程编程是一种以过程为中心的编程思想,分析出解决问题的步骤,然后用函数把这些...面向对象编程是将事物对象化,通过与对象进行通信来解决问题。面向对象编程,数据和对数据的操作是绑定在一起的。
  • Java面向对象编程

    千次阅读 2018-07-02 13:16:14
    桂 林 理 工 大 学 实 验 报 告 班级 软件工程 学号 ****** 姓名 ***** 同组...实验名称 Java面向对象编程 日期 208年06 月07日 一、实验目的: 1. 理解 Java 语言是如何体现面向对象编程基本思想的; 2. 掌...
  • C++是面向对象编程,C语言是面向过程编程。因此学习C++的第一件事就是要弄明白什么是面向过程编程,什么是面向对象编程。 之前学习的C语言是面向过程编程,回想一下C语言编程的过程。主函数,定义变量,调用函数...
  • OOP 面向对象编程

    千次阅读 2019-12-09 11:34:20
    //实际编码实现阶段则是面向对象编程[OOP] 在面向对象设计这一环节中,我们将需求描述转化为具体的类的设计。 这个环节的工作可以拆分为下面四个部分。 1. 划分职责进而识别出有...
  • 其实目的还是为了跟上一些主流编程语言的脚步,例如 java 、C++ 、Python,他们内部都是用 class 语法来实现的面向对象编程,所以咱们的 JavaScript 也不能落后,不然很多学习过 java c++ python 的小伙伴跑来学习 ...
  • 什么是面向过程编程?  面向过程编程(Procedure Oriented,OPP,面向对象程序设计)是一种以过程为中心的编程思想。“面向过程”也可称之为“面向记录”编程思想,他们不支持丰富的“面向对象...什么是面向对象编程
  • 面向对象编程(Python版详解)

    千次阅读 多人点赞 2020-04-03 14:14:18
    面向对象编程介绍二.类和对象三.类的构成 一.面向对象编程介绍 如今主流的软件开发思想有两种:一个是面向过程,另一个是面向对象。面向过程出现得较早,典型代表为C语言,开发中小型项目的效率很高,但是很难适用...
  • 面向对象编程的思想

    万次阅读 热门讨论 2020-09-12 16:12:00
    面向对象思想 前言        计算机的革命起源于机器,所以说,编程语言的产生也是始于对机器的模仿。在19世纪的早期就已经出现过计算机的雏形。那时,人们为了构建导航所需的表格对...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 129,279
精华内容 51,711
关键字:

面向对象编程