黑马程序员——7K面试

------ Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

       先解释一下这篇博客的题目。之所以称为7K面试题,就是用人单位承诺如果做出这道面试题目就为面试者提供月薪7000元的工作,因此从薪水的角度来说是非常有吸引力的,但从另一方面来说,这道面试题必然具有一定的难度,而这样的面试方式更能够考察面试者的综合开发能力。

那么这一篇博客中,我们将介绍两个面试题目,由于这两道面试题需要设计两个完整的系统,因此需要详细介绍题意、设计思路,以及相关代码的编写。在此之前,我们通过几个例子来加强面向对象的设计思想,而这一思想对于完成前述系统设计面试题目是非常必要的。

1  面向对象设计思想加强

       题目一:“人在黑板上画圆”,应该将画圆的方法定义在谁身上?

       思路:上述过程中,涉及到3个对象,分别是人、黑板和圆。面向对象的设计思想中一个非常重要的原则就是谁拥有数据谁就要提供操作该数据的方法。想要画出一个圆,需要两个数据,一个是圆心坐标,另一个是半径。而这两个数据都是属于圆对象的,因此操作这两个数据画出圆的方法就要定义在圆自己身上。

       题目二:列车司机刹车。刹车的方法应该定义在哪个对象身上?

       思路:从表面上看,是司机把列车停了下来,但实际上司机并没有那么大的力气,把正在高速行驶的列车刹住。那么司机做的事情只不过是给列车下达了停车的命令,而真正使列车刹车的是列车本身,因为只有列车自己才清楚如何抱闸、如何停止引擎的运转等等。

       题目三:售货员统计收获小票上的总金额。那么计算总金额的方法又应该定义在哪个对象身上呢?

       思路:现实生活中总金额的计算确实是售货员利用计算器或者通过心算计算出来的,但是当我们需要用面向对象的对象进行描述时,就要遵循面向对象设计的原则。每个商品的价格是依次打印在售货小票上的,因此商品价格数据是属于售货小票这个对象的,那么计算总金额的方法当然也是要定义在收获小票对象上。

       题目四:在“人关门”的情景中,“关闭”方法应该定义在哪个对象上?

       思路:这个题目与“列车司机刹车”是类似的。人对于门来说,只是一个命令的下达者,也就是说,关门的动作实际是门自己完成的,比如,门轴是如何旋转的、门锁是如何自动锁上的等等,这些都是门自己最清楚,因此“关闭”方法自然而然就要定义在“门”对象身上。那么最终实现关门的效果,当然还需要“关闭”方法的调用者,而这个调用者就是“人”。

       题目五:“两块石头磨成一把石刀,石刀可以砍树,砍下来的树就变成木材,最终可以将木材做成椅子”。将上述过程通过面向对象的思想,转换为代码编写出来。

       思路:上述表述中涉及到多个对象,包括石头、石刀、树、木材,以及椅子。这些对象之间是相互转换的关系,这些转换的动作可以定义在这些对象本身,比如石刀砍树的动作肯定要定义在石刀本身,而有些动作就需要第三方对象的参与,比如石头转换为石刀,这一转换动作不应该定义在石头上,因为石头不能自己把自己变成石刀,这不符合现实规律,这一转换过程对于石头来说是一个被动的过程,因此需要第三方对象。这里我们可以定义一个石刀工厂(KnifeFactory),专门对外提供将石头转换为石刀的方法等等。

       依照上述设计思路我们给出如下伪代码例程。

代码1:

Stone firstStone = new Stone();
Stone secondStone = new Stone();
//石刀工厂,将石头转换为石刀
Knife knife =KnifeFactory.createNewKnife(stone);
 
Tree tree = new Tree();
//石刀具备砍树功能
Woods wood = knife.cut(tree);
 
//椅子工厂,将木材转换为椅子
Chair chair = ChairFactory.createNewChair(wood);
代码说明:

(1)  由于石刀工厂并不涉及该类的特有数据,因此可以将产生一个新石刀的方法定义为一个静态方法,接收一个石头对象,经方法内部转换(这里不给出具体代码),返回一个石刀对象。按照现实情况石头、树木等原材料并不是创建出来的,但这里为了突出重点,将石头和树木的获取过程简化为创建对象。此外,椅子的创建同样通过椅子工厂类将木材转换为椅子的静态方法来实现。

(2)  对于木材的产生,可以调用石刀对象的cut方法,方法需要传递树木对象作为参数,并返回木材对象。

       题目六:“一个小球从一根绳子的一端移动到另一端”。同样将上述过程通过面向对象的思想,转换为代码编写出来。

       思路:小球和绳子之间本身是没有任何关系的,但现在题目要求这两个事物之间产生一个关系,那么就要思考这是一个怎样的关系。一个物体的运动总是通过物体所处位置的变换来体现的。小球从位置A变换到位置B就是一次移动。假设我们将小球所处的位置简化为一个点,那么从题意可知这个点是由绳子提供的,因为小球是沿着绳子运动的。那么这个时候,小球和绳子之间就产生了一个关系——小球按照绳子提供的位置移动,或者说绳子为小球的移动提供了方向。在这个关系中,位置就是数据,根据谁拥有数据就要提供对数据进行操作方法的思想,绳子应该对外提供返回下一个位置的方法。由于小球要移动,因此应具备一个移动方法,而方法参数就是绳子提供的位置。

       根据上述设计思路,给出如下伪代码例程。

代码2:

//将“位置”这类事物抽象为一个类
ppublic class Point {}
//Rope类
public class Rope {
	//绳子的起始位置,以及结束位置
	private Point start;
	private Point end;
	//在构造函数中初始化起始位置和结束位置
	public Rope(Point start, Point end) {
		this.start = start;
		this.end = end;
	}
   
	public Point nextPoint(Point currentPoint) {
		/*
		 * 通过某种算法给出相对当前位置的下一个位置
		 * */
		return new Point();
	}
}
//小球类
public class Ball {
	//将绳子内化为小球对象的成员变量
	private Rope rope;
	//currentPoint记录小球对象的当前位置
	private Point currentPoint;
   
	public Ball(Rope rope, Point point) {
		this.rope = rope;
		this.currentPoint = point;
	}
   
	//“移动”方法,方法参数为小球的当前的位置
	//将小球移动到Rope对象返回的相对当前位置给出的下一个位置
	//将下一个位置打印到控制台表示移动
	public void move(Point currentPoint) {
		currentPoint = rope.nextPoint(currentPoint);
		System.out.println("小球移动到了"+currentPoint+"位置处");
	}
}
代码说明:

(1)  Point:将“位置”这一类事物抽象为一个类,称为Point。此类的主要作用就是对外提供一个位置坐标,比如X轴坐标值,以及Y轴坐标值等等,由于这里仅作演示,因此不给出具体的代码。

(2)  Rope:将绳子这类事物定义为Rope类。Rope中定义两个Point类型的成员变量,分别表示绳子的起始位置,以及结束位置,这两个成员变量可以在Rope的构造方法中进行初始化,这样就创建了一个新的绳子对象。而Rope的主要方法就是给出相对当前位置的下一个位置,方法参数是由外部传递的当前位置,同样不给出具体的计算方法。

(3) Ball:分别定义Rope类型,以及Point类型的成员变量,这两者均在构造方法中进行初始化。其中Point对象表示当前位置,而Rope对象的作用就是根据当前位置计算下一个位置,然后Ball的move方法就根据外部传入的当前位置,得到下一个位置,并将下一个位置打印到控制台表示小球的移动。

2  7K面试题之一——交通灯系统

2.1  题目

       模拟实现十字路口的交通灯管理系统逻辑,具体需求如下:

(1)  异步随机生成按照各个路线行驶的车辆。

       例如:

              由南向而来去往北向的车辆——直行车辆

              由西向而来去往南向的车辆——右转车辆

              由东向而来去往南向的车辆——左转车辆

              …

(2)  信号灯忽略黄灯,只考虑红灯和绿灯。

(3)  应考虑左转车辆控制信号灯,右转车辆不受信号灯控制。

(4)  具体信号灯控制逻辑与现实生活中普通交通灯控制逻辑相同,不考虑特殊情况下的控制在逻辑。注:南北向车辆与东西向车辆交替放行,同方向等待车辆应先放行直行车辆而后放行左转车辆。

(5)  每辆车通过路口时间为1秒(提示,可通过线程Sleep的方法模拟)。

(6)  随机生成车辆时间间隔以及红绿灯交换时间间隔自定,可以设置。

(7)  不要求实现GUI,只考虑系统逻辑实现,可通过Log方式展现程序运行结果。

2.2  题意分析

(1)  路线分析

       任何一个十字路口,总是分为东南西北4个方向,这4个方向之间进行两两组合总共可以产生3种行走方式,12条路线,3中行走方式分别是:直行,左转和右转。其中直行和左转需要受到交通灯的控制,而右转则不需要。直行路线分别是:南到北、北到南、东到西、西到东;左转路线分别是:南到西、东到南、北到东、西到北;右转路线分别是:南到东、东到北、北到西、西到南。上述12条路线如下图所示。


       关于上图我们首先解释一下每个路线的命名方式,以“S2N”为,2的英文单词“two”与“to”同音,因此代表了汉字“到”,整个意思就是从南到北的这条路线,其他路线的名称也是相同的命名方式。以上就是一个普通十字路口通常所具有的12条路线。路线分析完毕,我们接着根据路线分析一下,路线对应红绿灯的变化情况。

(2)  红绿灯变化分析

       上图虽然乍看起来非常复杂混乱,但是可以进行一定的简化。比如,“S2N”与“N2S”路线的红绿灯是同时变化的,因此我们只需考虑其中一个路线红绿灯的亮灭情况即可。同理,“S2W”与“N2E”这两条左转路线红绿灯也是同时变化的,因此只需考虑其中一条路线。这样一来,除去右转线路,原来的8条直行、左转路线,现在只需要控制其中4条就可以就可以实现十字路口的正常运行。这里我们选择以下4个方向作为主要控制路线,直行方向:“S2N”和“E2W”;左转方向:“S2W”和“E2S”(这4条路线的选择,只是个人喜好,大家可以选择自己习惯的4个方向,只要方便分析思考即可)。

       现在我们来分析右转路线的控制。可能有朋友认为右转路线由于不受红绿灯的控制,因此就不设置红绿灯,这就需要为12条路线设计两种红绿灯模型——有灯模型与无灯模型,较为繁琐。为了简化设计,将模型统一起来,只需将右转路线的红绿灯设置为常绿即可,而其他路线的红绿灯设置为随时间变换。

       还有一点非常重要的是,我们需要搞清楚红绿灯的变化顺序,上图中为4条路线标上了序号,这一序号是路线对应红绿灯的变化顺序。比如,两个南北向路线(“S2N”和“N2S”)的红绿灯由绿变为红以后,下一个变灯的路线并非是两个东西向路线(“E2W”和“W2E”),而是左转路线,分别是“S2W”和“N2E”,之后才是两个东西向直行路线,接着又是一对左转路线的红绿灯变换,就此完成了一个红绿灯变化循环,那么这一变化循环的不断重复就是十字路口交通灯正常的运行过程。上述内容可以简单总结为:某个方向的车都走完了,无论直行还是拐弯,才轮到另外方向的车。

2.3  面向对象的分析与设计

       根据上述题意分析的内容,我们可以从中提取出涉及其中的若干事物。首先是红绿灯对象,这个不必多说;其次是用于控制红绿灯变换的一个控制装置,这一控制装置的存在也是符合实际情况的,因为红绿灯是不会自己变化的;再次,还要设计上述12条路线;最后,就是在路面上行驶的车辆。下面我们就逐一分析上述4个事物的设计思路,并给出相应的代

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值