CodeGym/Java 博客/随机的/面向对象的原则
John Squirrels
第 41 级
San Francisco

面向对象的原则

已在 随机的 群组中发布
个会员
Java 是一种面向对象的语言。这意味着您需要使用面向对象的范例来编写 Java 程序。这个范例需要在你的程序中使用对象和类。让我们尝试通过示例来了解什么是类和对象,以及如何在实践中应用基本的 OOP 原则(抽象、继承、多态和封装)。

什么是对象?

我们生活的世界是由物体组成的。环顾四周,我们可以看到我们被房屋、树木、汽车、家具、餐具和电脑所包围。所有这些东西都是对象,它们中的每一个都有一组特定的特征、行为和目的。我们习惯于对象,我们总是将它们用于非常特定的目的。例如,如果我们需要上班,我们会开车。如果我们想吃东西,我们就用盘子。如果我们想休息,我们会找到一张舒适的沙发。人类习惯于从对象的角度来思考,以解决日常生活中的问题。这是在编程中使用对象的原因之一。这种方法称为面向对象编程。让我们举个例子。想象一下,您已经开发出一款新手机并希望开始量产。作为手机的开发者,你知道它的用途,它是如何工作的,它的部件是什么(机身、麦克风、扬声器、电线、按钮等)。更重要的是,只有您知道如何连接这些部件。但你不打算亲自制作手机——你有一整队工人来做这件事。为了避免重复解释如何连接手机的部件,并确保所有手机都以相同的方式制作,在开始生产之前,您需要绘制一张描述手机如何组织的图纸。在 OOP 中,我们将这样的描述、绘图、图表或模板称为类。它构成了程序运行时创建对象的基础。类是对特定类型对象的描述——就像由字段、方法和构造函数组成的通用模板。对象是类的实例。

抽象

现在让我们考虑如何从现实世界中的对象转移到程序中的对象。我们将以电话为例。这种通信方式已有 100 多年的历史。现代电话是一种比其 19 世纪的前辈复杂得多的设备。使用电话时,我们不会考虑它的结构和内部发生的过程。我们只需使用手机开发商提供的功能:按钮或触摸屏即可输入电话号码并拨打电话。最早的电话接口之一是需要旋转才能打电话的曲柄。当然,这不是很方便。但它完美地完成了它的功能。如果你比较最现代的手机和最早的手机,您可以立即识别出对 19 世纪末的设备和现代智能手机最重要的功能。它们是拨打电话的能力和接听电话的能力。事实上,这就是使电话成为电话的原因,而不是别的什么。现在只是应用了一个OOP的原则:确定一个对象最重要的特征和信息。这个原则被称为抽象。在 OOP 中,抽象也可以定义为一种将现实世界任务的元素表示为程序中的对象的方法。抽象总是与对象的某些属性的泛化相关联,因此最主要的是在手头任务的上下文中将有意义的信息与无关紧要的信息分开。此外,可以有多个抽象级别。让' 让我们尝试将抽象原理应用到我们的手机上。首先,我们将确定最常见的手机类型——从最早的手机到现在的手机。例如,我们可以用图 1 中的图表形式表示它们。 面向对象的原则 - 2使用抽象,我们现在可以识别此对象层次结构中的一般信息:一般抽象对象(电话)、电话的共同特征(例如,它的创建年份)和公共接口(所有电话都可以接听和拨打电话)。这是它在 Java 中的样子:
public abstract class AbstractPhone {
    private int year;

    public AbstractPhone(int year) {
        this.year = year;
    }
    public abstract void call(int outgoingNumber);
    public abstract void ring(int incomingNumber);
}
在一个程序中,我们可以使用这个抽象类并应用 OOP 的其他基本原则来创建新型电话,我们将在下面探讨这些原则。

封装

通过抽象,我们可以确定所有对象的共同点。但每种手机都是独一无二的,在某种程度上与其他手机不同。在程序中,我们如何划定界限并识别这种个性?我们如何做到这一点,以至于没有人会意外或故意损坏我们的手机或尝试将一种型号转换为另一种型号?在现实世界中,答案很明显:你需要把所有的零件都放在一个手机壳里。毕竟,如果您不这样做——而是将手机的所有内部部件和连接线留在外面——一些好奇的实验者肯定会想要“改进”我们的手机。为了防止这种修补,在对象的设计和操作中使用了封装原则。这个原则指出一个对象的属性和行为被组合在一个单一的类中,对象' 其内部实现对用户是隐藏的,并提供了一个公共接口来处理该对象。程序员的任务是确定对象的哪些属性和方法应该可供公共访问,哪些是应该不可访问的内部实现细节。

封装和访问控制

假设有关手机的信息(其生产年份或制造商的徽标)在制造时刻在其背面。信息(其状态)特定于此特定模型。我们可以说制造商确保此信息是不可更改的——任何人都不太可能想去除雕刻。在 Java 世界中,类使用字段描述未来对象的状态,使用方法描述它们的行为。使用应用于字段和方法的修饰符控制对对象状态和行为的访问:私有、受保护、公共和默认。例如,我们决定生产年份、制造商名称和其中一种方法是类的内部实现细节,不能被程序中的其他对象更改。在代码中,
public class SomePhone {

    private int year;
    private String company;
    public SomePhone(int year, String company) {
        this.year = year;
        this.company = company;
    }
private void openConnection(){
    // findSwitch
    // openNewConnection...
}
public void call() {
    openConnection();
    System.out.println("Calling");
}

public void ring() {
    System.out.println("Ring-ring");
}

 }
private 修饰符允许类的字段和方法只能在此类中访问。这意味着无法从外部访问私有字段,因为无法调用私有方法。限制对openConnection方法的访问还使我们能够自由更改该方法的内部实现,因为该方法保证不会被其他对象使用或中断其他对象的工作。为了使用我们的对象,我们使用 public 修饰符使callring方法可用。提供用于处理对象的公共方法也是封装的一部分,因为如果访问被完全拒绝,它将变得毫无用处。

遗产

让我们再看一下电话图。您可以看到它是一个层次结构,其中模型具有沿其分支位于较高位置的模型的所有功能,并添加了一些自己的功能。例如,智能手机使用蜂窝网络进行通信(具有手机的属性),无线便携(具有无绳电话的属性),可以接听和拨打电话(具有电话的属性)。我们在这里拥有的是对象属性的继承。在编程中,继承意味着使用现有的类来定义新的类。让我们考虑一个使用继承来创建智能手机类的示例。所有无绳电话均由可充电电池供电,具有一定的电池寿命。因此,我们将此属性添加到无绳电话类:
public abstract class CordlessPhone extends AbstractPhone {

    private int hour;

    public CordlessPhone (int year, int hour) {
        super(year);
        this.hour = hour;
    }
    }
手机继承了无绳电话的属性,我们在这个类中实现了callring方法:
public class CellPhone extends CordlessPhone {
    public CellPhone(int year, int hour) {
        super(year, hour);
    }

    @Override
    public void call(int outgoingNumber) {
        System.out.println("Calling " + outgoingNumber);
    }

    @Override
    public void ring(int incomingNumber) {
        System.out.println("Incoming call from " + incomingNumber);
    }
}
最后,我们有智能手机类,与传统手机不同,它拥有成熟的操作系统。您可以通过添加可在其操作系统上运行的新程序来扩展智能手机的功能。在代码中,该类可以描述如下:
public class Smartphone extends CellPhone {

    private String operationSystem;

    public Smartphone(int year, int hour, String operationSystem) {
        super(year, hour);
        this.operationSystem = operationSystem;
    }
public void install(String program) {
    System.out.println("Installing " + program + " for " + operationSystem);
}

}
如您所见,我们创建了大量新代码来描述Smartphone类,但我们得到了一个具有新功能的新类。OOP 的这一原则可以显着减少所需的 Java 代码量,从而使程序员的工作更轻松。

多态性

尽管各种电话的外观和设计各不相同,但我们可以确定一些共同的行为:它们都可以接听和拨打电话,并且都有一套相当清晰和简单的控件。在编程方面,抽象原则(我们已经很熟悉了)让我们可以说电话对象有一个共同的接口。这就是为什么人们可以轻松使用具有相同控件(机械按钮或触摸屏)的不同型号的手机,而无需深入研究设备的技术细节。因此,您经常使用手机,并且可以轻松地从朋友的座机上拨打电话。OOP 的原理是说程序可以使用具有公共接口的对象而不需要任何有关对象内部结构的信息,这称为多态性。让' 想象一下,我们需要我们的程序来描述可以使用任何电话呼叫另一个用户的用户。我们可以这样做:
public class User {
    private String name;

    public User(String name) {
        this.name = name;
            }

    public void callAnotherUser(int number, AbstractPhone phone){
// And here's polymorphism: using the AbstractPhone type in the code!
        phone.call(number);
    }
}
 }
现在我们将描述几种电话。最早的手机之一:
public class ThomasEdisonPhone extends AbstractPhone {

public ThomasEdisonPhone(int year) {
    super(year);
}
    @Override
    public void call(int outgoingNumber) {
        System.out.println("Crank the handle");
        System.out.println("What number would you like to connect to?");
    }

    @Override
    public void ring(int incomingNumber) {
        System.out.println("The phone is ringing");
    }
}
普通座机:
public class Phone extends AbstractPhone {

    public Phone(int year) {
        super(year);
    }

    @Override
    public void call(int outgoingNumber) {
        System.out.println("Calling " + outgoingNumber);
    }

    @Override
    public void ring(int incomingNumber) {
        System.out.println("The phone is ringing");
    }
}
最后,一个很酷的视频电话:
public class VideoPhone extends AbstractPhone {

    public VideoPhone(int year) {
        super(year);
    }
    @Override
    public void call(int outgoingNumber) {
        System.out.println("Connecting video call to " + outgoingNumber);
    }
    @Override
    public void ring(int incomingNumber) {
        System.out.println("Incoming video call from " + incomingNumber);
    }
  }
我们将在main()方法中创建对象并测试callAnotherUser()方法:
AbstractPhone firstPhone = new ThomasEdisonPhone(1879);
AbstractPhone phone = new Phone(1984);
AbstractPhone videoPhone=new VideoPhone(2018);
User user = new User("Jason");
user.callAnotherUser(224466, firstPhone);
// Crank the handle
// What number would you like to connect to?
user.callAnotherUser(224466, phone);
// Calling 224466
user.callAnotherUser(224466, videoPhone);
// Connecting video call to 224466
在用户对象 上调用相同的方法会产生不同的结果。call方法的具体实现是在callAnotherUser()方法内部根据程序运行时传入的具体对象类型动态选择的。这是多态的主要优势——在运行时选择实现的能力。在上面给出的电话类示例中,我们使用了方法覆盖——一种我们在不更改方法签名的情况下更改基类中定义的方法的实现的技巧。这实质上取代了方法:在程序执行时调用子类中定义的新方法。通常,当我们重写一个方法时,@Override使用注释。它告诉编译器检查被覆盖和覆盖方法的签名。最后,为确保您的 Java 程序符合 OOP 原则,请遵循以下提示:
  • 识别对象的主要特征;
  • 识别公共属性和行为并在创建类时使用继承;
  • 使用抽象类型来描述对象;
  • 尝试始终隐藏与类的内部实现相关的方法和字段。
评论
  • 受欢迎
你必须先登录才能发表评论
此页面还没有任何评论