JAVA面向对象编程艺术与思想:打印日历3

来源:互联网 发布:如何进入淘宝二手市场 编辑:程序博客网 时间:2024/06/04 19:19

小方格(元素)CalElement

把各种类型的格子共同的特点归纳出来,写成calElement类:

x,y代表了格子的位置

x代表第几列,y代表第几行

content代表每个格子内显示的内容

每个格子都可以打印,所有需要重写toString方法,这样就可以直接打印格子内容

不同类型的格子有不同的计算位置方法,所以我把计算坐标方法,设置为了抽象方法,这样就强行让子类去实现

因为该类是各种小格子元素的父类,所以我把它设置为了抽象类。


package org.lrf.Calendar;/** * 日历的显示元素 * @author 33css.com * */public abstract class CalElement{public CalElement() {}public CalElement(String content) {this(content,0,0);}public CalElement(String content,int x,int y) {this.content = content;this.x = x;this.y = y;}/** * 该元素的显示内容 */String content;int x; //该日在日历上的坐标int y; //该日在日历上的坐标public int getY() {return y;}public void setY(int y) {this.y = y;}@Overridepublic String toString() {return this.content.toString();}/** * 计算坐标 */public abstract void calculateCoordinate();}











标题元素


标题显示在第一行,第一列所以计算坐标方法,设置他们的坐标为1,1,在构造该对象时候调用该方法

提供传参构造方法


package org.lrf.Calendar;/** * 显示标题元素 *  * @author 33css * */public class TitleElement extends CalElement {/** * 构造函数给content对象属性赋值 *  * @param content */public TitleElement(String content) {super(content);calculateCoordinate();}/** * 标题显示在第一行第一列 */@Overridepublic void calculateCoordinate() {this.x = 1;this.y = 1;}}







星期元素


为了避免硬编码,我面先来建立(日,一,二,三,四,五,六)DayId枚举

枚举包含id属性和内容属性(0:日     1:一       2:二       3:三       4:四      5:五       6:六

package org.lrf.Calendar;import java.util.Arrays;import java.util.List;/** * 显示星期信息需要用到当前枚举 * @author 33css.com * */public enum DayId {SUN(0,"日"),MON(1,"一"),TUS(2,"二"),WED(3,"三"),THU(4,"四"),FRI(5,"五"),SAT(6,"六");private  int id;private String content;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}private DayId(int id,String content) {this.id = id;this.content = content;}/** * 获取所有枚举 * @return 所有枚举 */public static List<DayId> getAll(){return Arrays.asList(DayId.values());}}



星期元素类型的构造方法传递1个DayId枚举类型的值,来确定当前格子是星期几,然后根据该枚举值来计算当前格子的x坐标和格子中显示的内容。

该行紧随标题以后,所以是第二行,y等于2。


package org.lrf.Calendar;public class DayElement extends CalElement{public DayElement(DayId dayId) {super();this.content = dayId.getContent()+"\t";this.dayId = dayId;calculateCoordinate();}private DayId dayId;public DayId getDayId() {return this.dayId;}public void setDayId(DayId dayId) {this.dayId = dayId;}@Overridepublic void calculateCoordinate() {this.x = this.dayId.getId();this.y = 2;//星期信息显示在第2行}}





空元素


因为第一周的第一天不一定是从周日开始的,所以一旦不是从周日开始,前面就需要空的元素来占位。

该元数的内容都为空,我们用空格占位符代表\t。

位置信息五福通过内容来确定,因为内容都是空,所以我们是通过构造方法传入位置信息。

我们不需要实现计算坐标方法,就让实现空着。

package org.lrf.Calendar;public class EmptyElement extends CalElement{public EmptyElement(int x,int y) {super("\t",x,y);}/** * 因为在构造函数中已经设定x和y所以不需要调用下面的方法 */@Overridepublic void calculateCoordinate() {}}


日期元素




每一个格子(元素),代表一个日期,所以该类包含一个Calendar对象属性。

构造方法需要传入一个Calender对象,来设置该类的Calendar对象属性。使用clone方法来克隆一个新的Calendar对象,以免改变传入的Calendar对象。(引用传参)

根据Calendar对象属性的当月日期来确定该元素显示的内容(content对象属性)。calendar.get(Calendar.DAY_OF_MONTH)


获取Calendar对象属性的DayOfWeek值(所在星期的第几天,星期天为第一天)来确定元素的x坐标。在第几列。

获取Calendar对象属性的WeekOfMonth值(所在月的第几个星期,星期天为第一天)来确定元素的y坐标。在第几行。因为标题元素和星期元素已经在用两行,所以在当前行数加2



package org.lrf.Calendar;import java.util.Calendar;/** * 日历上的某个日期元素,继承于CalElement,对象属性 Calendar代表当前日期 * 可以定位到在日历上的所在位置 * @author 33css.com * */public class DateElement extends CalElement{public DateElement(Calendar calendar) {super();this.calendar = (Calendar)calendar.clone(); //设置calendar对象属性this.content = calendar.get(Calendar.DAY_OF_MONTH)+"/t"; //设置显示内容calculateCoordinate();}private Calendar calendar; //当日calendar对象public Calendar getCalendar() {return calendar;}public void setCalendar(Calendar calendar) {this.calendar = calendar;}/** * 打印元素内容 */@Overridepublic String toString() {return this.calendar.get(Calendar.DATE)+"\t";}/** * 计算日期的坐标 */@Overridepublic void calculateCoordinate() {this.x = this.calendar.get(Calendar.DAY_OF_WEEK);  //获取当日在所在星期的第几天,作为x坐标this.y = this.calendar.get(Calendar.WEEK_OF_MONTH)+2;    //获取当日在所在月的第几个星期,作为y左边(星期天是第一天),第一行是标题,第二行是星期,所以需要加2从第三行开始计算}}


元素控制面板

现在我们把所有的实体类都建立好了(4种类型的元素和1个根元素)。我们需要有个一个对象来建立,管理,显示这些元素,因此我们建立了元素控制面板类。


我们需要建立一个对象属性elements来存放所有日期对象(存放小格子),因此我们建立了List来存放所有CalElement对象。


写4个方法来分别创建每一种元素对象,并放入elements对象属性中

createTitleElement()创建标题元素

createDayElements()创建星期元素

createEmptyElements()创建空元素

createDateElements()创建日期


编写print方法来循环打印所有的元素,elements



package org.lrf.Calendar;import java.util.ArrayList;import java.util.Calendar;import java.util.GregorianCalendar;import java.util.List;public class ElementControlPanel {/** * 创建elements 数组 elements数组拥有没有日历元素的显示坐标 * @param year 要打印的年份 * @param month 要打印的月份 */public ElementControlPanel(int year,int month) {this.calendar = new GregorianCalendar(year,month-1,1);//因为月份是从0开始计算所以先对月份参数-1setElements(); //创建需要打印的所有日历元素并把元素放入对象属性elements中}private List<CalElement> elements; //需要展示的所有日历元素数组private Calendar calendar; //需要显示的日期/** * 把日历元素打印到控制台 */public void print() {int currentRow = 1;for (CalElement calElement : elements) { //循环所有元素并打印if(currentRow != calElement.getY()) { //如果y所代表的行号改变,则把局部变量加一,并且打印换行符currentRow++;System.out.print("\n");}System.out.print(calElement.toString()); //打印元素}}/** * 获取所传参数那个月的最后一天 * @param calendar  * @return */private Calendar getLastDayOfMonth(Calendar calendar) {Calendar lastCalendar = new GregorianCalendar(calendar.get(Calendar.YEAR),calendar.get(Calendar.MONTH),1); lastCalendar.roll(Calendar.DATE, -1);//日期回滚一天,也就是最后一天       return lastCalendar;}/** * 设置elements List,需要三种类型的calElement(包括:标题,星期,空元素,日期) */private void setElements(){elements = new ArrayList<>();this.elements.add(createTitleElement()); //把 标题  加入要显示的元素数组this.elements.addAll(createDayElements()); //把 星期  加入要显示的元素数组this.elements.addAll(createEmptyElements());//把 空格  加入要显示的元素数组this.elements.addAll(createDateElements());//把 日期  加入要显示的元素数组}/** * 创建title元素,使用stringbuffer比拼string效率更好 * @return 创建的title元素 */private CalElement createTitleElement(){StringBuffer content = new StringBuffer();content.append(String.valueOf(this.calendar.get(Calendar.YEAR)));content.append("年\t");content.append(String.valueOf(this.calendar.get(Calendar.MONTH)+1));content.append("月");return new TitleElement(content.toString());}/** * 创建星期元素 * @return 所有星期元素 */private List<CalElement> createDayElements(){List<CalElement> result = new ArrayList<>();for (DayId dayId : DayId.getAll()) {result.add(new DayElement(dayId));}return result;}/** * 创建Date元素数组, * @return 当月所有日期元素 */private List<CalElement> createDateElements() {List<CalElement> result = new ArrayList<>();int lastDayOfMonth = getLastDayOfMonth(this.calendar).get(Calendar.DATE); //当月最后一天的日期for (int i = 0; i < lastDayOfMonth; i++) {DateElement dateElement = new DateElement(this.calendar);result.add(dateElement);this.calendar.add(Calendar.DATE, 1); //加一天}this.calendar.set(Calendar.DATE, 1);return result;}/** * 创建空元素(如果当月的第一天不是星期天,那么在日历上第三行,也就是日期的第一行,前半部分会显示空行) * @return 空元素集合 */private List<CalElement> createEmptyElements() {List<CalElement> result = new ArrayList<>();int firstDateOfWeak = this.calendar.get(Calendar.DAY_OF_WEEK);if (firstDateOfWeak == 0) {return result;}for(int i = 0;i < firstDateOfWeak-1;i++) {result.add(new EmptyElement(i+1, 3));}return result;}}

入口方法

创建ElementControllorPanel对象,调用print打印方法,在控制台查看打印结果。

package org.lrf.Calendar;public class App {//入口方法public static void main(String[] args) {new ElementControlPanel(2017, 9).print();}}




上面就是我们通过面向对象的思想来编写日历。

可能你会说,我没有看出面向对象的好处,很正常,因为你是看不出代码可扩展性的。

面向对象的编程思想已经极大增强了代码的可扩展性,降低了耦合度。

试着去增加更多的项目需求,扩展你的项目,让我们看看面向对象的真正威力吧。


请看下集













原创粉丝点击