软考:软件设计师 — 12.面向对象程序设计

十二. 面向对象程序设计

面向对象程序设计是下午场中的最后一道自选题,可以选择 Java 或 C++,分值 15 分,建议拿到 6 分以上。该类题型以代码填空的形式出现。

1. Java 语法要点

类的定义

[import 包]

[类修饰符] class xxxclass [extends 父类] / [implements 接口] {

      public:公有数据成员或公有函数成员的定义;

      protected(默认):保护数据成员或保护函数成员的定义;

      private:私有数据成员或私有函数成员的定义

}

  • import 包:引入包中类。
  • 类修饰符:常用的有,public、protected、private、abstract、final、interface 等。
  • class 为关键字,xxxclass 为类名,命名遵循 Java 标识符的命名规则。
  • extends 为继承关键字,implements 为接口关键字。

例如:

① class CashDiscount implements CashSuper(接口名) { }

② class Whip extends CondimentDecorator(父类名){ }

abstract class Beverage {

       public abstract int cost ();

}

④ class Department {/* 代码省略 */ }

    class SqlserverDepartment extends Department {/* 代码省略 */ }

abstract class Shape { abstract public void draw () }

    class Rectangle extends Shape { public void draw () }

接口的定义

[修饰符] inferface 接口名 [extends 父接口名列表] {

         [public] [static] [final] 常量;

         [public] [abstract] 方法;

}

所有方法都没有具体实现,也就是说接口中都是抽象方法,在声明方法时可以不加 abstract 修饰。

例如:

interface Drawing {

public void drawLine (double x1, double y1, double x2, double y2);

public void drawCircle (double x, double y, double r);

}

class V1Drawing implements Drawing {

     public void drawLine (double x1, double y1, double x2, double y2) { /* 代码省略 */}

     public void drawCircle (double x, double y, double r) { /* 代码省略 */}

}

2. 注意事项及代码填空技巧

大小写问题

Java 语言大小写敏感,如:interface 写成 Interface 是错误的。

什么时候加 this

  • set 方法中,引用对象的变化

class EnvironmentData implements Environment {

    private float temperature, humidity, cleanness;

    public void setMeasurements (float temperature, float humidity, float cleanness) {

        this.temperature = temperatrue; 总不能写成 temperatrue = temperatrue吧。

        ……}

……}

  • 带参数的构造函数

class CashDiscount implements CashSuper {

     private double moneyDiscount;

     public CashDiscount (double moneyDiscount) { //构造函数(与类同名)

       this.moneyDiscount = moneyDiscount;

}

父类与子类(接口与实现类)之间方法的统一

  • 对于方法填空,主要依据父类与子类(接口与实现类)之间方法的统一;
  • 注意对于接口抽象方法,Java 没有方法体(无花括号)。

注意函数调用

  • 代码中可能出现方法体的缺失,这一类填空可以根据函数传参,从而判断该参数类型、该类型对象能够调用的方法,进而填空;
  • 代码中可能出现方法形参(参数列表)的缺失,这一类填空需要根据方法体判断该方法需要使用的参数名,根据调用的过程判断该参数的类型。

代码填空技巧

  • 关键字填空

经常出现关键字填空,如 abstract、extends、implements 等,注意不要出现拼写错误。

  • 缺失方法填空
  1. 注意父类与子类、接口与实现类之间的统一,经常出现对应方法的缺失,可以根据上下文进行填写,注意接口方法无方法体,其它返回值类型、参数列表应该保持一致;
  2. 注意构造函数的写法,get 和 set 方法的应用;
  3. 如果 Java 自带的一些类型方法,需要靠平时积累,多做真题。
  • 实例化填写

注意实例化的参数选择。

类名  对象名 = new 类名();父类名 对象名 = new 子类名()

  • 注意函数调用(参数列表、参数类型、对应参数能够调用的方法)
  • 注意程序设计题与设计模式的结合,多做真题。

3. 例题

(1)例题 1

说明:

某图书管理系统中管理着两种类型的文献:图书和论文。现在要求统计所有馆藏文献的总页码(假设图书馆中有一本 540 页的图书和两篇各 25 页的论文,那么馆藏文献的总页码就是 590 页)。采用 访问者(Visitor)模式实现该要求,得到如下所示的类图。

Java 代码:

import java.util.*;

interface LibraryVisitor{
  (1)___;
  (2)___;
  void printSum();
}

class LibrarySumPrintVisitor implements LibraryVisitor{
  print int sum = 0;
  public void visit(Book p_book){
    sum = sum + p_book.getNumberOfPages(); 
  }
  public void visit(Article p_article){
    sum = sum + p_article.getNumberOfPages(); 
  }
  public void printSum(){
    System.out.println("SUM=" + sum );
  }
}

interface LibraryItemInterface{
  (3)___;
}

class Article implements LibraryItemInterface{
  private String m_title;
  private String m_author;
  private int m_start_page;
  private int m_end_page;
  public Article(String p_title, String p_author, int p_start_page, int p_end_page){
    m_title = p_title;
    m_author = p_author;
    m_start_page = p_start_page;
    m_end_page = p_end_page;
  }
  public int getNumberOfPages(){
    return m_end_page - m_start_page;
  }
  public void accept(LibraryVisitor visitor){
  (4)___;
  }
} 

class Book implements LibraryItemInterface{
  private String m_title;
  private String m_author;
  private int m_start_page;
  private int m_end_page;
  public Book(String p_title, String p_author, int p_start_page, int p_end_page){
    m_title = p_title;
    m_author = p_author;
    m_start_page = p_start_page;
    m_end_page = p_end_page;
  }
  public int getNumberOfPages(){
    return m_end_page - m_start_page;
  }
  public void accept(LibraryVisitor visitor){
  (5)___;
  }
}

解析:

第(1)(2)空明显是补充接口 LibraryVisitor 缺失的方法,注意接口中的方法均为抽象方法,没有具体实现,直接根据下面实现类找到缺失的方法即可,因为实现类需要实现接口中的抽象方法。显然是下方的 LibrarySumPrintVisitor 类实现了 接口 LibraryVisitor,printSum 方法不缺失,缺失的是 public void visit(Book p_book) 和 public void visit(Article p_article) 两个方法,因此(1)(2)空分别填写上述两个方法即可。

第(3)空是补充接口 LibraryItemInterface 缺失的方法,因此首先找到实现它的具体类,即 Book 和 Article,它们的构造方法肯定不能是抽象方法,还剩下 getNumberOfPages 方法和 accept 方法。此处是一个常见考点,由于题干中已经说明采用访问者模式来实现,因此需要实现访问者模式中特有的 accept 方法,它是访问者模式中的应用代码,不可缺失,并且 getNumberOfPages 方法是用来获取内部属性,一般不拿来抽象。所以,第(3)空填写 public void accept(LibraryVisitor Visitor)

第(4)(5)空是对 accept 方法体的补充,也就是其执行过程。accept 方法使用的参数是 LibraryVisitor 接口对象,该接口提供了三种方法,visit(Book p_book),visit(Article p_article) 和 printSum(),visitor 对象能够访问的只有 LibraryVisitor 对象中的参数方法,并且 visit(Article p_article) 方法恰好是访问了本类的对象,所以第(4)(5)空填写的就是对本类对象的访问,即 visitor.visit(this),this 即代表 Article 和 Book 对应的对象。

(1)public void visit(Book p_book)

(2)public void visit(Article p_article)

(3)public void accept(LibraryVisitor Visitor)

(4)visitor.visit(this)

(5)visitor.visit(this)

前两个空是送分题,这 6 分必拿,剩余的需要分析题目与靠平时积累。

(2)例题 2

说明:

某快餐厅主要制作并出售儿童套餐,一般包括主餐(各类披萨)、饮料和玩家,其餐品种类可能不同,但其制作过程相同。前台服务员(Waiter)调度厨师制作套餐。现采用生成器(Builder)模式实现制作过程,得到如下所示类图。

Java 代码:

class Pizza{
  private String parts;
  public void setParts(String parts){ 
    this.parts = parts; 
  }
  public String toString(){
    return this.parts;
  }
}


abstract class PizzaBuilder{
  protected Pizza pizza;
  public Pizza getPizza(){
    return pizza;
  }
  public void createNewPizza(){
    pizza = new Pizza();
  }
  public (1)___;
}

class HawaiianPizzaBuilder extends PizzaBuilder{
  public void builderParts(){
    pizza.setParts("cross + mild + ham&pineapple");
  }
}

class SpicyPizzaBuilder extends PizzaBuilder{
  public void builderParts(){
    pizza.setParts("pan baked + hot + pepperoni&salami");
  }
}

class Waiter{
  private PizzaBuilder pizzaBuilder;
  public void setPizzaBuilder(PizzaBuilder pizzaBuilder){ // 设置构造器
    (2)___;
  }
  public Pizza getPizza(){
    return pizzaBuilder.getPizza();
  }
  public void construct(){ // 构造
    pizzaBuilder.createNewPizza();
    (3)___;
  }
}

class FastFoodOrdering{
  public static void main(String[] args){
    Waiter waiter = new Waiter();
    PizzaBuilder hawaiian_pizzabuilder = new HawaiianPizzaBuilder();
    (4)___;
    (5)___;
    System.out.println("pizza:" + waiter.getPizza());
  }
}

程序输出结果:pizza:cross + mild + ham&pineapple

解析:

第(1)空为补充抽象类 PizzaBuilder 中缺失的方法,首先找到实现它的具体类,即 HawaiianPizzaBuilder 和 SpicyPizzaBuilder,这两个类均实现了 builderParts 方法,因此该方法只能是从抽象类中继承来的,所以抽象类 PizzaBuilder 中缺失的方法就是 builderParts,但注意,抽象类中暂且没有抽象方法,并且第(1)空缺失的方法也没有具体实现,所以它应该是抽象方法,因此填写 abstract void builderParts()。(或者记住关于抽象类中至少需要有一个抽象方法,仅限于软考中该题型而言)

第(2)(3)空均在 Waiter 类中,第(2)空涉及到 set 方法的属性值补充,首先考虑到 this 关键字,并且属性值名称为 pizzaBuilder,传给它的参数值也叫 pizzaBuilder,因此使用 this,即 this.pizzaBuilder = pizzaBuilder。第(3)空需要填写 construct 方法中缺失的属性,由于 Waiter 类中使用的均是 PizzaBuilder 抽象类中的参数方法,并且已经使用过了 getPizza 方法和 createNewPizza 方法,还剩下一个 builderParts 方法,所以 construct 方法中缺失的就是 builderParts 方法,即 pizzaBuilder.builderParts()。

第(4)(5)空均在主类中,也就是涉及到对上述几个类的具体使用。首先实例化了一个 Waiter 对象和 HawaiianPizzaBuilder 的对象,并且最后使用 getPizza 方法输出结果,那么缺失的部分就是设置构造器并使用构造器,即 setPizzaBuilder 方法和 construct 方法,前者需要参数,后者不需要,并且是基于 waiter 对象进行构造,因此填写 waiter.setPizzaBuilder(hawaiian_pizzabuilder) 和 waiter.construct()。

(1)abstract void builderParts()

(2)this.pizzaBuilder = pizzaBuilder

(3)pizzaBuilder.builderParts()

(4)waiter.setPizzaBuilder(hawaiian_pizzabuilder)

(5)waiter.construct()

(3)例题 3

说明:

某软件系统中,已设计并实现了用于显示地址信息的类 Address,现要求提供基于 Dutch 语言的地址信息显示接口。为了实现该要求并考虑到以后可能还会出现新的语言的接口,决定采用适配器(Adapter)模式实现该要求,得到如下所示类图。

Java 代码:

import java.util.*;

class Address{
  public void street(){ //实现省略}
  public void zip(){}
  public void city(){}
}

class DutchAddress{
  public void straat(){}
  public void postcode(){}
  public void plaats(){}
}

class DutchAddressAdapter extends DutchAddress{
  private (1)___;
  public DutchAddressAdpter(Address addr){
     address = addr;
  }
  public void straat(){
     (2)___;
  }
  public void postcode(){
     (3)___;
  }
  public void plaats(){
     (4)___;
  }
}

class Test{
  public static void main(String[] args){
    Address addr = new Address();
    (5)___;
    System.out.println("\n The DutchAddress \n");
    testDutch(addrAdapter);
  }
   
  Static void testDutch(DutchAddress addr){
     addr.straat();
     addr.postcode();
     addr.plaats();
  }
}

解析:

第(1)空是补充子类 DutchAddressAdapter 继承父类 DutchAddress 后需要的属性值,下面给出了其构造函数,缺少的是被传递的对象,即将 addr 传递给 address,并且均属于 Address 类,所以填写 Address address。

第(2)(3)(4)空的填写需要能够看出一个对应关系。所谓适配器模式也就是接口的转换,在本题中就将 DutchAddress 转换成 Address,同时根据单词含义和拼写也能大概了解到这类对应,即 street = straat,zip = postcode,city = plaats。那么这三个空缺失的就是对应的转换,并且基于 address 对象,即 address.street(),address.zip(),address.city()。

第(5)空在主类中,并且完成了对 Address 类的实例化,那么缺失的就是对适配器的实例化,而且最后也使用了对象 addrAdapter 进行测试,那么实例化对象的名称就是 addrAdapter。由于是对适配器的实例化,因此考虑父类与子类间的实例化过程,即 父类名 名称 = new 子类名(),也就是 DutchAddress addrAdapter = new DutchAddressAdapter(),注意,该子类是带参数的,所以不要忘记它的参数,即一个 Address 类的对象,也就是上方实例化的 addr,因此完整的填写为DutchAddress addrAdapter = new DutchAddressAdapter(addr)。

(1)Address address

(2)address.street()

(3)address.zip()

(4)address.city()

(5)DutchAddress addrAdapter = new DutchAddressAdapter(addr)

(4)例题 4

说明:

欲开发一个绘图软件,要求使用不同的绘图程序绘制不同的图形。以绘制直线和圆形为例,对应的绘图程序如下表所示。

该绘图软件的扩展性要求,将不断扩充新的图形和新的绘图程序。为了避免出现类爆炸的情况,现采用桥接(Bridge)模式来实现上述要求,得到如下所示类图。

 

Java 代码:

(1)___ Drawing{
(2)___;
(3)___;
}

class DP1{
  static public void draw_a_line(double x1, double y1, double x2, double y2){//实现省略}
  static public void draw_a_circle(double x, double y, double r){}
}

class DP2{
  static public void drawline(double x1, double x2, double y2, double y2){//实现省略}
  static public void drawcircle(double x, double y, double r){}
}

class V1Drawing implements Drawing{
  public void drawLine(double x1, double y1, double x2, double y2){}
  public void drawCircle(double x, double y, double r){
  (4)___;
  }
}

class V2Drawing implements Drawing{
  public void drawLine(double x1, double y1, double x2, double y2){}
  public void drawCircle(double x, double y, double r){
  (5)___;
  }
}

abstract class Shape{
  private Drawing dp;
  (6)___;
  Shape(Drawing dp){this.dp = dp;}
  public void drawLine(double x1, double y1, double x2, double y2 {
  dp.drawLine(x1,y1,x2,y2);
  }
  public void drawCircle(double x, double y, double r){
  dp.drawCircle(x,y,r);
  }
}

class Rectangle extends Shape{
  private double _x1,_x2,_y1,_y2;
  public Rectangle(Drawing dp, double x1, double y1, double x2, double y2){//实现省略}
  public void draw(){//实现省略}
}

class Circle extends Shape{
  private double _x,_y,_r;
  public Circle(Drawing dp, double x, double y, double r){}
  public void draw(){}
}

解析:

第(1)(2)(3)空一开始看不知道是什么,但下面有 V1Drawing 和 V2Drawing 实现了 Drawing,那么 Drawing 就是一个接口,所以第(1)空填 interface,那么(2)(3)就是接口中的抽象方法,也就是子类中罗列的两个方法,drawLine 和 drawCircle,因此分别填写 public void drawLine(double x1, double y1, double x2, double y2) 和 public void drawCircle(double x, double y, double r),注意,由于是抽象方法,所以没有具体实现,即没有花括号。

根据题干可知,第(4)(5)空分别是 DP1 和 DP2 绘图软件绘制圆形时的方法调用,因为 V1Drawing 使用 DP1 绘图软件,V2Drawing 使用 DP2 绘图软件。DP1 绘制圆形的方法是 draw_a_circle(double x, double y, double r),DP2 是 drawCircle(double x, double y, double r)。所以,两空分别填写 DP1.draw_a_circle(double x, double y, double r),DP2.drawCircle(double x, double y, double r)。

第(6)空是在抽象类 Shape 中,记住抽象类中至少有一个抽象方法,此处缺少一个抽象方法。下面 Rectangle 和 Circle 类继承抽象类 Shape,并且实现了一个名为 draw 的方法,那么 draw 就是抽象类 Shape 中缺失的抽象方法,即 public abstract void draw()。

(1)interface

(2)public void drawLine(double x1, double y1, double x2, double y2)

(3)public void drawCircle(double x, double y, double r)

(4)DP1.draw_a_circle(x, y, r)

(5)DP2.drawCircle(x, y, r)

(6)public abstract void draw()

第(4)(5)空带不带 double 在实际运算时不受影响,但题干中第一个表中给出的两个方法是没有具体类型的,因此按照题干中的表格填写。

面向对象程序设计部分的内容至此结束,后续如果有补充或修改会直接添加。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Phoenixxxxxxxxxxxxx

感谢支持!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值