案例:计算每位顾客的消费金额并打印详细信息。顾客租赁了哪些影片,租期多长,根据租赁时间和影片类型计算出费用。影片分为3类:儿童片,新片,普通片。此外需计算该顾客的积分。

Movie:

  1. public class Movie {
  2. //电影类型
  3. public static final int CHILD = 2;
  4. public static final int NEW = 3;
  5. public static final int REGULAR = 1;
  6. private String _title;
  7. private int _priceCode;
  8.  
  9. public Movie(String title,int priceCode) {
  10. this._title = title;
  11. this._priceCode = priceCode;
  12. }
  13.  
  14. public String get_title() {
  15. return _title;
  16. }
  17. /**
  18. * 获取影片类型
  19. * @return
  20. */
  21. public int get_priceCode() {
  22. return _priceCode;
  23. }
  24. }

Resume:该顾客租赁了一部影片

  1. public class Resume {
  2. private Movie _movie;
  3. private int _daysRented;
  4.  
  5. public Resume(Movie movie,int daysRented) {
  6. this._movie = movie;
  7. this._daysRented = daysRented;
  8. }
  9.  
  10. public Movie get_movie() {
  11. return _movie;
  12. }
  13.  
  14. public int get_daysRented() {
  15. return _daysRented;
  16. }
  17.  
  18. }

Customer:

租赁费用计算:

影片类型为儿童片,两天以内费用为2,超出两天的时间,每天的费用为1.5

影片类型为新片,每天的费用为3

影片类型为普通片,三天以内费用为1.5,超出三天,每天的费用为1.5

积分计算:

每次租赁影片,积分加一,如果影片为新片且租赁时间大于1天,则多加一分

  1. import java.util.Enumeration;
  2. import java.util.Scanner;
  3. import java.util.Vector;
  4.  
  5. public class Customer {
  6. private String _name;
  7. private Vector<Resume> _resume = new Vector<Resume>(); //all resume by this customer
  8.  
  9. public Customer(String name){
  10. this._name = name;
  11. }
  12.  
  13. /**
  14. * add resume info
  15. * @param arg
  16. */
  17. public void addRental(Resume arg){
  18. this._resume.addElement(arg);
  19. }
  20.  
  21. public String getName(){
  22. return this._name;
  23. }
  24.  
  25. /**
  26. * get all result(include time,movie type,fee of each resume and all fee)
  27. * @return result
  28. */
  29. public String statement(){
  30. double totalAmount = 0;
  31. int frequentRenterPoints = 0; //the all collectPoint;
  32. Enumeration<Resume> resumes = this._resume.elements(); //all record of resumes
  33. String result = "Rental Record for" +"\t" + this.getName() + "\n";
  34. while(resumes.hasMoreElements()){
  35. double thisAmount = 0; // fee of this record
  36. Resume each = (Resume) resumes.nextElement();
  37. // the movie's type
  38. switch(each.get_movie().get_priceCode()){
  39. case Movie.CHILD:
  40. thisAmount += 2; //the basic fee is 2
  41. if(each.get_daysRented() > 2){
  42. //the day is more than 2
  43. thisAmount += (each.get_daysRented() - 2) * 1.5;
  44. }
  45. break;
  46. case Movie.NEW:
  47. thisAmount += each.get_daysRented() * 3;
  48. break;
  49. case Movie.REGULAR:
  50. thisAmount += 1.5; //the basic fee is 1.5
  51. if(each.get_daysRented() > 3){
  52. //the day is more than 3
  53. thisAmount += (each.get_daysRented() - 3) * 1.5;
  54. }
  55. break;
  56. }
  57. frequentRenterPoints ++;
  58. if((each.get_movie().get_priceCode() == Movie.NEW)&&(each.get_daysRented() > 1)){
  59. frequentRenterPoints ++;
  60. }
  61. result += "\t" + each.get_movie().get_title() + "\t" + String.valueOf(thisAmount) + "\n";
  62. totalAmount += thisAmount;
  63. }
  64.  
  65. result += "Amount owed is" + "\t" + String.valueOf(totalAmount) + "\n";
  66. result += "You earned "+ String.valueOf(frequentRenterPoints) + " frequent renter points";
  67. return result;
  68. }
  69.  
  70. @SuppressWarnings("resource")
  71. public static void main(String arg[]){
  72. Scanner sc = new Scanner(System.in);
  73. System.out.println("please input your name:"+"\n");
  74. String c_name = sc.nextLine();
  75. Customer c1 = new Customer(c_name);
  76.  
  77. System.out.println("please input the movie name:"+"\n");
  78. String m_name = sc.nextLine();
  79. System.out.println("please input the movie type:"+ "\n");
  80. System.out.println("1.regular movie"+"\n"+"2.child movie"+"\n"+"3.new movie"+"\n");
  81. int type = sc.nextInt();
  82. Movie m1 = new Movie(m_name,type);
  83. System.out.println("please input the time you have rent:"+"\n");
  84. int day = sc.nextInt();
  85. Resume r1 = new Resume(m1,day);
  86. c1.addRental(r1);
  87. String ans= c1.statement();
  88. System.out.println(ans);
  89. }
  90. }

现在的代码可以实现基本的功能,当租赁策略、积分策略发生改变时,需要仔细查找statement策略,这时很容易引入bug。那么就很有必要重构之前写的代码。

第一步:为即将修改的代码建立一个可靠的测试环境。

MovieTest

  1. import static org.junit.Assert.*;
  2.  
  3. import org.junit.After;
  4. import org.junit.Before;
  5. import org.junit.Test;
  6.  
  7. public class MovieTest {
  8. Movie m0 = new Movie("fall in love",3);
  9.  
  10. @Before
  11. public void setUp() throws Exception {
  12. }
  13.  
  14. @After
  15. public void tearDown() throws Exception {
  16. }
  17.  
  18. @Test
  19. public void testGet_title() {
  20. assertEquals("fall in love",m0.get_title());
  21. }
  22.  
  23. @Test
  24. public void testGet_priceCode() {
  25. assertEquals(3,m0.get_priceCode());
  26. }
  27.  
  28. }

ResumeTest

  1. import static org.junit.Assert.*;
  2.  
  3. import org.junit.After;
  4. import org.junit.Before;
  5. import org.junit.Test;
  6.  
  7. public class ResumeTest {
  8. Movie m2 = new Movie("three children and their mother",2);
  9. Resume r2 = new Resume(m2,3);
  10.  
  11. @Before
  12. public void setUp() throws Exception {
  13. }
  14.  
  15. @After
  16. public void tearDown() throws Exception {
  17. }
  18.  
  19. @Test
  20. public void testGet_movie() {
  21. Movie m3 = new Movie("three children and their mother",2);
  22. assertEquals(m3.get_title(),r2.get_movie().get_title());
  23. }
  24.  
  25. @Test
  26. public void testGet_daysRented() {
  27. assertEquals(r2.get_daysRented(),3);
  28. }
  29.  
  30. }

CustomerTest

  1. import static org.junit.Assert.*;
  2. import org.junit.Test;
  3.  
  4. public class CustomerTest {
  5. Movie m1 = new Movie("123435",1);
  6. Resume r1 = new Resume(m1,4);
  7. Customer c1 = new Customer("abby");
  8.  
  9. @Test
  10. public void testAddRental() {
  11. c1.addRental(r1);
  12. }
  13.  
  14. @Test
  15. public void testGetName() {
  16. String testname = "abby";
  17. assertEquals(testname, c1.getName());
  18. }
  19.  
  20. @Test
  21. public void testStatement() {
  22. String testResult = "Rental Record for abby"+"\n\t"+
  23. "123435 3.0"+"\n"+
  24. "Amount owed is 3.0"+"\n"+
  25. "You earned 1 frequent renter points";
  26. c1.addRental(r1);
  27. String realResult = c1.statement();
  28. assertEquals(testResult,realResult);
  29. }
  30. }

 第二步:分解重组代码块

statement函数太长了,我们需要分解它,首先将switch语句包装到另外一个函数AmountFor中去,并更改名称使代码更加容易理解

  1. /**
  2. * calculate amount fee for this resume
  3. * @param resume
  4. * @return
  5. */
  6. private double AmountFor(Resume resume){
  7. double result = 0; // fee of this record
  8. switch(resume.get_movie().get_priceCode()){
  9. case Movie.CHILD:
  10. result += 2; //the basic fee is 2
  11. if(resume.get_daysRented() > 2){
  12. //the day is more than 2
  13. result += (resume.get_daysRented() - 2) * 1.5;
  14. }
  15. break;
  16. case Movie.NEW:
  17. result += resume.get_daysRented() * 3; //the basic fee is 2
  18. break;
  19. case Movie.REGULAR:
  20. result += 1.5; //the basic fee is 1.5
  21. if(resume.get_daysRented() > 3){
  22. //the day is more than 3
  23. result += (resume.get_daysRented() - 3) * 1.5;
  24. }
  25. break;
  26. }
  27. return result;
  28. }

AmountFor

原来的statement函数改为下面的代码

  1. /**
  2. * get all result(include time,movie type,fee of each resume and all fee)
  3. * @return result
  4. */
  5. public String statement(){
  6. double totalAmount = 0;
  7. int frequentRenterPoints = 0; //the all collectPoint;
  8. Enumeration<Resume> resumes = this._resume.elements(); //all record of resumes
  9. String result = "Rental Record for" +"\t" + this.getName() + "\n";
  10. while(resumes.hasMoreElements()){
  11. Resume each = resumes.nextElement();
  12. // get amount for each resume
  13. double thisAmount = this.AmountFor(each);
  14. frequentRenterPoints ++;
  15. if((each.get_movie().get_priceCode() == Movie.NEW)&&(each.get_daysRented() > 1)){
  16. frequentRenterPoints ++;
  17. }
  18. result += "\t" + each.get_movie().get_title() + "\t" + String.valueOf(thisAmount) + "\n";
  19. totalAmount += thisAmount;
  20. }
  21.  
  22. result += "Amount owed is" + "\t" + String.valueOf(totalAmount) + "\n";
  23. result += "You earned "+ String.valueOf(frequentRenterPoints) + " frequent renter points";
  24. return result;
  25. }

Statement

在AmountFor中我们发现它只使用了Resume类,并没有使用到Movie,所以我们将AmountFor函数放在Resume类中,并将函数名改为GetCharge

  1. public class Resume {
  2. ......
  3. /**
  4. * calculate charge for this resume
  5. * @return
  6. */
  7. public double GetCharge(){
  8. double result = 0; // fee of this record
  9. switch(get_movie().get_priceCode()){
  10. case Movie.CHILD:
  11. result += 2; //the basic fee is 2
  12. if(get_daysRented() > 2){
  13. //the day is more than 2
  14. result += (get_daysRented() - 2) * 1.5;
  15. }
  16. break;
  17. case Movie.NEW:
  18. result += get_daysRented() * 3; //the basic fee is 2
  19. break;
  20. case Movie.REGULAR:
  21. result += 1.5; //the basic fee is 1.5
  22. if(get_daysRented() > 3){
  23. //the day is more than 3
  24. result += (get_daysRented() - 3) * 1.5;
  25. }
  26. break;
  27. }
  28. return result;
  29. }
  30.  
  31. }

GetCharge

同时添加新的函数测试代码

  1. public class ResumeTest {
  2. ......
  3. @Test
  4. public void testGetCharge() {
  5. assertEquals(String.valueOf(r2.GetCharge()),String.valueOf(3.5));
  6. }
  7. }

testGetCharge

然后在原来的程序中找到旧函数的所有引用点,然后再用新函数去代替他们

接下来类似“费用计算”我们处理“积分计算”,直接显示修改后的代码

  1. public class Resume {
  2. ......
  3.  
  4. /**
  5. * get FrequentRenterPoints for this resume
  6. * @return
  7. */
  8. public int GetFrequentRenterPoints(){
  9. int result = 0;
  10. result ++;
  11. if((get_movie().get_priceCode() == Movie.NEW)&&(get_daysRented() > 1)){
  12. result ++;
  13. }
  14. return result;
  15. }
  16.  
  17. }

GetFrequentRenterPoints

  1. /**
  2. * get all result(include time,movie type,fee of each resume and all fee)
  3. * @return result
  4. */
  5. public String statement(){
  6. double totalAmount = 0;
  7. int frequentRenterPoints = 0; //the all collectPoint;
  8. Enumeration<Resume> resumes = this._resume.elements(); //all record of resumes
  9. String result = "Rental Record for" +"\t" + this.getName() + "\n";
  10. while(resumes.hasMoreElements()){
  11. Resume each = resumes.nextElement();
  12. frequentRenterPoints += each.GetFrequentRenterPoints();
  13. totalAmount += each.GetCharge();
  14. result += "\t" + each.get_movie().get_title() + "\t" + String.valueOf(each.GetCharge()) + "\n";
  15. }
  16.  
  17. result += "Amount owed is" + "\t" + String.valueOf(totalAmount) + "\n";
  18. result += "You earned "+ String.valueOf(frequentRenterPoints) + " frequent renter points";
  19. return result;
  20. }

statement

然后接着提取totalAmount和totalFrequentRenterPoints

  1. public class Customer {
  2. ......
  3.  
  4. /**
  5. * get total charge
  6. * @return
  7. */
  8. private double GetTotalCharge(){
  9. Enumeration<Resume> resumes = this._resume.elements(); //all record of resumes
  10. double result = 0;
  11. while(resumes.hasMoreElements()){
  12. Resume each = resumes.nextElement();
  13. result += each.GetCharge();
  14. }
  15. return result;
  16. }
  17.  
  18. /**
  19. * get total frequentRenterPoints
  20. * @return
  21. */
  22. private int GetTotalFrequentRenterPoints(){
  23. Enumeration<Resume> resumes = this._resume.elements(); //all record of resumes
  24. int result = 0;
  25. while(resumes.hasMoreElements()){
  26. Resume each = resumes.nextElement();
  27. result += each.GetFrequentRenterPoints();
  28. }
  29. return result;
  30. }
  31.  
  32. /**
  33. * get all result(include time,movie type,fee of each resume and all fee)
  34. * @return result
  35. */
  36. public String statement(){
  37. Enumeration<Resume> resumes = this._resume.elements(); //all record of resumes
  38. String result = "Rental Record for" +"\t" + this.getName() + "\n";
  39. while(resumes.hasMoreElements()){
  40. Resume each = resumes.nextElement();
  41. result += "\t" + each.get_movie().get_title() + "\t" + String.valueOf(each.GetCharge()) + "\n";
  42. }
  43.  
  44. result += "Amount owed is" + "\t" + String.valueOf(GetTotalCharge()) + "\n";
  45. result += "You earned "+ String.valueOf(GetTotalFrequentRenterPoints()) + " frequent renter points";
  46. return result;
  47. }
  48.  
  49. }

Customer

最后测试一下修改后的代码

现在你会发现statement函数所做的功能全部是字符串拼接,即界面显示工作,如果需要将结果显示成HTML或者是其他形式,直接添加相同功能函数即可。

第三步:使用类的特性(分装,继承,多态)和设计模式对程序继续重构

switch部分很容易发生修改,因为在修改影片费用策略时就会修改到switch部分,我们现在来重构switch部分

switch部分最好是在自己对象上使用,尽可能的避免在别人的对象上使用。所以这就提示我们需要把switch部分移到movie类中

  1. public class Movie {
  2. ......
  3.  
  4. /**
  5. * calculate charge for resume
  6. * @return
  7. */
  8. public double GetCharge(int dayRent){
  9. double result = 0; // fee of this record
  10. switch(this.get_priceCode()){
  11. case Movie.CHILD:
  12. result += 2; //the basic fee is 2
  13. if(dayRent > 2){
  14. //the day is more than 2
  15. result += (dayRent - 2) * 1.5;
  16. }
  17. break;
  18. case Movie.NEW:
  19. result += dayRent * 3; //the basic fee is 2
  20. break;
  21. case Movie.REGULAR:
  22. result += 1.5; //the basic fee is 1.5
  23. if(dayRent > 3){
  24. //the day is more than 3
  25. result += (dayRent - 3) * 1.5;
  26. }
  27. break;
  28. }
  29. return result;
  30. }
  31.  
  32. /**
  33. * get FrequentRenterPoints for resume
  34. * @return
  35. */
  36. public int GetFrequentRenterPoints(int dayRent){
  37. if((get_priceCode() == Movie.NEW)&&(dayRent > 1))
  38. return 2;
  39. else
  40. return 1;
  41. }
  42. }

Movie

  1. public class Resume {
  2. ......
  3.  
  4. /**
  5. * calculate charge for this resume
  6. * @return
  7. */
  8. public double GetCharge(){
  9. return _movie.GetCharge(this._daysRented);
  10. }
  11.  
  12. /**
  13. * get FrequentRenterPoints for this resume
  14. * @return
  15. */
  16. public int GetFrequentRenterPoints(){
  17. return _movie.GetFrequentRenterPoints(_daysRented);
  18. }
  19.  
  20. }

Resume

影片类型有三种,而这三种影片的租赁价格都有其各自的计算方法,所以使用的是策略模式

下面是重构以后关于movie修改和新加的内容:

  1. public class Movie {
  2. //电影类型
  3. public static final int CHILD = 2;
  4. public static final int NEW = 3;
  5. public static final int REGULAR = 1;
  6. private String _title;
  7. private int _priceCode; //影片类型
  8. private Price _price;
  9.  
  10. public Movie(String title,int priceCode) {
  11. this._title = title;
  12. this._priceCode = priceCode;
  13. set_priceCode();
  14. }
  15.  
  16. public String get_title() {
  17. return _title;
  18. }
  19.  
  20. public int get_priceCode() {
  21. return _price.getPriceCode();
  22. }
  23.  
  24. public void set_priceCode() {
  25. switch(_priceCode){
  26. case Movie.CHILD:
  27. _price = new ChildPrice();
  28. break;
  29. case Movie.NEW:
  30. _price = new NewPrice();
  31. break;
  32. case Movie.REGULAR:
  33. _price = new RegularPrice();
  34. break;
  35. }
  36. }
  37.  
  38. /**
  39. * calculate charge for resume
  40. * @return
  41. */
  42. public double GetCharge(int dayRent){
  43. return _price.getCharge(dayRent);
  44. }
  45.  
  46. /**
  47. * get FrequentRenterPoints for resume
  48. * @return
  49. */
  50. public int GetFrequentRenterPoints(int dayRent){
  51. if((get_priceCode() == Movie.NEW)&&(dayRent > 1))
  52. return 2;
  53. else
  54. return 1;
  55. }
  56. }

Movie

  1. public abstract class Price {
  2. abstract int getPriceCode();
  3. abstract double getCharge(int dayRent);
  4. }

Price

  1. public class NewPrice extends Price {
  2.  
  3. @Override
  4. int getPriceCode() {
  5. // TODO Auto-generated method stub
  6. return Movie.NEW;
  7. }
  8.  
  9. @Override
  10. double getCharge(int dayRent) {
  11. return dayRent * 3;
  12. }
  13.  
  14. }

NewPrice

  1. public class RegularPrice extends Price {
  2.  
  3. @Override
  4. int getPriceCode() {
  5. // TODO Auto-generated method stub
  6. return Movie.REGULAR;
  7. }
  8.  
  9. @Override
  10. double getCharge(int dayRent) {
  11. double result = 1.5; //the basic fee is 1.5
  12. if(dayRent > 3){ //the day is more than 3
  13. result += (dayRent - 3) * 1.5;
  14. }
  15. return result;
  16. }
  17.  
  18. }

RegularPrice

  1. public class ChildPrice extends Price {
  2.  
  3. @Override
  4. int getPriceCode() {
  5. // TODO Auto-generated method stub
  6. return Movie.CHILD;
  7. }
  8.  
  9. @Override
  10. double getCharge(int dayRent) {
  11. double result = 2; //the basic fee is 2
  12. if(dayRent > 2){ //the day is more than 2
  13. result += (dayRent - 2) * 1.5;
  14. }
  15. return result;
  16. }
  17.  
  18. }

ChildPrice

其实重构就是不断的测试修改的过程。

重构Java代码的既有设计-影片出租店的更多相关文章

  1. 怎样编写高质量的java代码

    代码质量概述     怎样辨别一个项目代码写得好还是坏?优秀的代码和腐化的代码区别在哪里?怎么让自己写的代码既漂亮又有生命力?接下来将对代码质量的问题进行一些粗略的介绍.也请有过代码质量相关经验的朋友 ...

  2. 敏捷开发中高质量 Java 代码开发实践

    Java 项目开发过程中,由于开发人员的经验.代码风格各不相同,以及缺乏统一的标准和管理流程,往往导致整个项目的代码质量较差,难于维护,需要较大的测试投入 和周期等问题. 这些问题在一个项目组初建.需 ...

  3. 如何在Android上编写高效的Java代码

    转自:http://www.ituring.com.cn/article/177180 作者/ Erik Hellman Factor10咨询公司资深移动开发顾问,曾任索尼公司Android团队首席架 ...

  4. 老司机告诉你高质量的Java代码是怎么练成的?

    一提起程序员,首先想到的一定是"码农",对,我们是高产量的优质"码农",我们拥有超跃常人的逻辑思维以及不走寻常路的分析.判别能力,当然,我们也有良好的编码规范, ...

  5. 写出优质Java代码的4个技巧(转)

    http://geek.csdn.net/news/detail/238243 原文:4 More Techniques for Writing Better Java 作者:Justin Alban ...

  6. java代码分析及分析工具

    一个项目从搭建开始,开发的初期往往思路比较清晰,代码也比较清晰.随着时间的推移,业务越来越复杂.代码也就面临着耦合,冗余,甚至杂乱,到最后谁都不敢碰. 作为一个互联网电子商务网站的业务支撑系统,业务复 ...

  7. 高效重构 C++ 代码

    引言 Martin Fowler的<重构:改善既有代码的设计>一书从2003年问世至今已有十几年时间了,按照计算机领域日新月异的变化速度,重构已经算是一门陈旧的技术了.但是陈旧并不代表不重 ...

  8. 怎样编写高质量的 Java 代码

    代码质量概述 怎样辨别一个项目代码写得好还是坏?优秀的代码和腐化的代码区别在哪里?怎么让自己写的代码既漂亮又有生命力?接下来将对代码质量的问题进行一些粗略的介绍.也请有过代码质量相关经验的朋友提出宝贵 ...

  9. 适用于Java开发人员的SOLID设计原则简介

    看看这篇针对Java开发人员的SOLID设计原则简介.抽丝剥茧,细说架构那些事——[优锐课] 当你刚接触软件工程时,这些原理和设计模式不容易理解或习惯.我们都遇到了问题,很难理解SOLID + DP的 ...

随机推荐

  1. javascript对象继承

    一.实例化和继承的区别 构造函数.原型和实例的关系:每 个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型 对象的内部指针. 类(Class)和实例(Insta ...

  2. 一.复习GCC编译器的用法

    1.复习GCC编译器的用法 欲善其工,那么要先利其器.在这个C语言巩固与提高的阶段中,如果想要更好的达成预期目标,首先就要熟练掌握GCC编译器的用法.以下是GCC相关知识: GCC使用语法 gcc 选 ...

  3. ipythons 使用攻略

    ipython是一个 python 的交互式 shell,比默认的 python shell 好用得多,支持变量自动补全,自动缩进,支持 bash shell 命令,内置了许多很有用的功能和函数. 安 ...

  4. [随记][asp.net基础]Page_Load和OnLoad

    标题:[随记][asp.net基础]Page_Load和OnLoad 一.前言 东西好久不用.不想,就会忘,所以没办法,只好记下来. 二.正文 aspx页面加载的时候会自动执行Page_Load,也会 ...

  5. opkg 不能更新和安装openwrt软件的方法

    首先,将所有的IPK 放在自己的虚拟HTTP服务器上.2,用Telnet进入路由器,使用VI编辑器,编程Opkg.conf,命令:       vi /etc/opkg.conf3,修改文件,将第一行 ...

  6. 20145329《Java程序设计》实验四总结

    实验四 Android环境搭建 实验内容 1.搭建Android环境 2.运行Android 3.修改代码,能输出学号 实验步骤 1.搭建Android环境 2.安装Android,核心是配置JDK. ...

  7. Calling Convention的总结

    因为经常需要和不同的Calling Convention打交道,前段时间整理了一下它们之间的区别,如下: 清理堆栈 参数压栈顺序 命名规则 (MSVC++) 备注 Cdecl 调用者 (Caller) ...

  8. Linux禁止普通用户使用crontab命令

    cron计划任务默认root用户与非root用户都可以执行,当然如果在安全方面想禁用这部分用户,则可以通过两个文件来解决: cron.allow cron.deny cron.allow:定义允许使用 ...

  9. 第八篇:Spark SQL Catalyst源码分析之UDF

    /** Spark SQL源码分析系列文章*/ 在SQL的世界里,除了官方提供的常用的处理函数之外,一般都会提供可扩展的对外自定义函数接口,这已经成为一种事实的标准. 在前面Spark SQL源码分析 ...

  10. rsync | scp文件同步命令使用

    现在有一台服务器A,目录/data2/abc下存在若干文件夹和文件,需要复制到服务器B中.这时,可以在服务器A上执行rsync或者scp命令,将文件夹或文件复制到服务器B中. SCP: scp /da ...