程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> 計算機程序的思維邏輯 (14),思維邏輯

計算機程序的思維邏輯 (14),思維邏輯

編輯:JAVA綜合教程

計算機程序的思維邏輯 (14),思維邏輯


正所謂,道生一,一生二,二生三,三生萬物,如果將二進制表示和運算看做一,將基本數據類型看做二,基本數據類型形成的類看做三,那麼,類的組合以及下節介紹的繼承則使得三生萬物。

上節我們通過類Point介紹了類的一些基本概念和語法,類Point中只有基本數據類型,但類中的成員變量的類型也可以是別的類,通過類的組合可以表達更為復雜的概念。

程序是用來解決現實問題的,將現實中的概念映射為程序中的概念,是初學編程過程中的一步跨越。本節通過一些例子來演示,如何將一些現實概念和問題,通過類以及類的組合來表示和處理。

我們先介紹兩個基礎類String和Date,他們都是Java API中的類,分別表示文本字符串和日期。

基礎類

String

String是Java API中的一個類,表示多個字符,即一段文本或字符串,它內部是一個char的數組,它提供了若干方法用於方便操作字符串。

String可以用一個字符串常量初始化,字符串常量用雙引號括起來(注意與字符常量區別,字符常量是用單引號),例如,如下語句聲明了一個String變量name,並賦值為"老馬說編程"

String name = "老馬說編程";

String類提供了很多方法,用於操作字符串。在Java中,由於String用的非常普遍,Java對它有一些特殊的處理,本節暫不介紹這些內容,只是把它當做一個表示字符串的類型來看待。

Date

Date也是Java API中的一個類,表示日期和時間,它內部是一個long類型的值,它也提供了若干方法用於操作日期和時間。

用無參的構造方法新建一個Date對象,這個對象就表示當前時間。

Date now = new Date();

日期和時間處理是一個比較長的話題,我們留待後續章節詳解,本節我們只是把它當做表示日期和時間的類型來看待。

圖形類

擴展 Point

我們先擴展一下Point類,在其中增加一個方法,計算到另一個點的距離,代碼如下:

public double distance(Point p){
    return Math.sqrt(Math.pow(x-p.getX(), 2)
            +Math.pow(y-p.getY(), 2));
}

線 - Line

在類型Point中,屬性x,y都是基本類型,但類的屬性也可以是類,我們考慮一個表示線的類,它由兩個點組成,有一個實例方法計算線的長度,代碼如下:

public class Line {
    private Point start;
    private Point end;
    
    public Line(Point start, Point end){
        this.start= start;
        this.end = end;
    }
    
    public double length(){
        return start.distance(end);
    }
}

Line由兩個Point組成,在創建Line時這兩個Point是必須的,所以只有一個構造方法,且需傳遞這兩個點,length方法計算線的長度,它調用了Point計算距離的方法獲取線的長度。可以看出,在設計線時,我們考慮的層次是點,而不考慮點的內部細節。每個類封裝其內部細節,對外提供高層次的功能,使其他類在更高層次上考慮和解決問題,是程序設計的一種基本思維方式。

使用這個類的代碼如下所示:

public static void main(String[] args) {
    Point start = new Point(2,3);
    Point end = new Point(3,4);
    
    Line line = new Line(start, end);
    System.out.println(line.length());
}

這個也很簡單。我們再說明一下內存布局,line的兩個實例成員都是引用類型,引用實際的point,整體內存布局大概如下圖所示:

start, end, line三個引用型變量分配在棧中,保存的是實際內容的地址,實際內容保存在堆中,line的兩個實例變量還是引用,同樣保存的是實際內容的地址。

電商概念

接下來,我們用類來描述一下電商系統中的一些基本概念,電商系統中最基本的有產品、用戶和訂單:

  • 產品:有產品唯一Id、名稱、描述、圖片、價格等屬性。
  • 用戶:有用戶名、密碼等屬性。
  • 訂單:有訂單號、下單用戶、選購產品列表及數量、下單時間、收貨人、收貨地址、聯系電話、訂單狀態等屬性。

當然,實際情況可能非常復雜,這是一個非常簡化的描述。

這是產品類Product的代碼:

public class Product {
    //唯一id
    private String id; 
    
    //產品名稱 
    private String name; 
    
    //產品圖片鏈接    
    private String pictureUrl; 
    
    //產品描述
    private String description;
    
    //產品價格
    private double price;
}

我們省略了類的構造方法,以及屬性的getter/setter方法,下面大部分示例代碼也都會省略。

這是用戶類User的代碼:

public class User {
    private String name;
    private String password;
}

一個訂單可能會有多個產品,每個產品可能有不同的數量,我們用訂單條目OrderItem這個類來描述單個產品及選購的數量,代碼如下所示:

public class OrderItem {
    //購買產品
    private Product product;
    
    //購買數量
    private int quantity;
    
    public OrderItem(Product product, int quantity) {
        this.product = product;
        this.quantity = quantity;
    }

    public double computePrice(){
        return product.getPrice()*quantity;
    }
}

OrderItem引用了產品類Product,我們定義了一個構造方法,以及計算該訂單條目價格的方法。

下面是訂單類Order的代碼:

public class Order {
    //訂單號
    private String id;
    
    //購買用戶
    private User user;
    
    //購買產品列表及數量
    private OrderItem[] items;
    
    //下單時間
    private Date createtime;
    
    //收貨人
    private String  receiver;
    
    //收貨地址
    private String address;
    
    //聯系電話
    private String phone;
    
    //訂單狀態
    private String status;
    
    public double computeTotalPrice(){
        double totalPrice = 0;
        if(items!=null){
            for(OrderItem item : items){
                totalPrice+=item.computePrice();
            }
        }
        return totalPrice;
    }
}

Order類引用了用戶類User,以及一個訂單條目的數組orderItems,它定義了一個計算總價的方法。這裡用一個String類表示狀態status,更合適的應該是枚舉類型,枚舉我們後續文章再介紹。

以上類定義是非常簡化的了,但是大概演示了將現實概念映射為類以及類組合的過程,這個過程大概就是,想想現實問題有哪些概念,這些概念有哪些屬性,哪些行為,概念之間有什麼關系,然後定義類、定義屬性、定義方法、定義類之間的關系,大概如此。概念的屬性和行為可能是非常多的,但定義的類只需要包括哪些與現實問題相關的就行了。

人 - Person

上面介紹的圖形類和電商類只會引用別的類,但一個類定義中還可以引用它自己,比如我們要描述人以及人之間的血緣關系,我們用類Person表示一個人,它的實例成員包括其父親、母親、和孩子,這些成員也都是Person類型。

下面是代碼:

public class Person {
    //姓名
    private String name;
    
    //父親
    private Person father;
    
    //母親
    private Person mother;
    
    //孩子數組
    private Person[] children;

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

這裡同樣省略了setter/getter方法。對初學者,初看起來,這是比較難以理解的,有點類似於函數調用中的遞歸調用,這裡面的關鍵點是,實例變量不需要一開始都有值。我們來看下如何使用。

public static void main(String[] args){
    Person laoma = new Person("老馬");
    Person xiaoma = new Person("小馬");
    
    xiaoma.setFather(laoma);
    laoma.setChildren(new Person[]{xiaoma});
    
    System.out.println(xiaoma.getFather().getName());
}

這段代碼先創建了老馬(laoma),然後創建了小馬(xiaoma),接著調用xiaoma的setFather方法和laoma的setChildren方法設置了父子關系。內存中的布局大概如下圖所示:


目錄和文件

接下來,我們介紹兩個類MyFile和MyFolder,分別表示文件管理中的兩個概念,文件和文件夾。文件和文件夾都有名稱、創建時間、父文件夾,根文件夾沒有父文件夾,文件夾還有子文件列表和子文件夾列表。

下面是文件類MyFile的代碼:

public class MyFile {
    //文件名稱
    private String name;
    
    //創建時間
    private Date createtime;
    
    //文件大小
    private int size;
    
    //上級目錄
    private MyFolder parent;

    //其他方法 ....
    
    public int getSize() {
        return size;
    }
}

下面是MyFolder的代碼:

public class MyFolder {
    //文件夾名稱
    private String name;
    
    //創建時間
    private Date createtime;
        
    //上級文件夾
    private MyFolder parent;
    
    //包含的文件
    private MyFile[] files;
    
    //包含的子文件夾
    private MyFolder[] subFolders;
    
    public int totalSize(){
        int totalSize = 0;
        if(files!=null){
            for(MyFile file : files){
                totalSize+=file.getSize();
            }
        }
        if(subFolders!=null){
            for(MyFolder folder : subFolders){
                totalSize+=folder.totalSize();
            }
        }
        return totalSize;
    }
    //其他方法...
}

MyFile和MyFolder,我們都省略了構造方法、settter/getter方法,以及關於父子關系維護的代碼,主要演示實例變量間的組合關系,兩個類之間可以互相引用,MyFile引用了MyFolder,而MyFolder也引用了MyFile,這個是沒有問題的,因為正如之前所說,這些屬性不需要一開始就設置,也不是必須設置的。另外,演示了一個遞歸方法totalSize(),返回當前文件夾下所有文件的大小,這是使用遞歸函數的一個很好的場景。

一些說明

類中定義哪些變量,哪些方法是與要解決的問題密切相關的,本節中並沒有特別強調問題是什麼,定義的屬性和方法主要用於演示基本概念,實際應用中應該根據具體問題進行調整。

類中實例變量的類型可以是當前定義的類型,兩個類之間可以互相引用,這些初聽起來可能難以理解,但現實世界就是這樣的,創建對象的時候這些值不需要一開始都有,也可以沒有,所以是沒有問題的。

類之間的組合關系,在Java中實現的都是引用,但在邏輯關系上,有兩種明顯不同的關系,一種是包含,另一種就是單純引用。比如說,在訂單類Order中,Order與User的關系就是單純引用,User是獨立存在的,而Order與OrderItem的關系就是包含,OrderItem總是從屬於某一個Order。
小結

對初學編程的人來說,不清楚如何用程序概念表示現實問題,本節通過一些簡化的例子來解釋,如何將現實中的概念映射為程序中的類。

分解現實問題中涉及的概念,以及概念間的關系,將概念表示為多個類,通過類之間的組合,來表達更為復雜的概念以及概念間的關系,是計算機程序的一種基本思維方式。

類之間的關系除了組合,還有一種非常重要的關系,那就是繼承,讓我們下節來探索繼承及其本質。

----------------

未完待續,查看最新文章,敬請關注微信公眾號“老馬說編程”(掃描下方二維碼),從入門到高級,深入淺出,老馬和你一起探索Java編程及計算機技術的本質。原創文章,保留所有版權。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved