这是我第一次接触面向对象,迷迷糊糊地做完了第一单元的作业。翻看自己这一单元的三次作业的代码,简直不忍直视,于是花了两天时间研究了一下创建型设计模式,因为也是第一次接触,难免理解不到位,下面我将结合这次代码需要来梳理一下常见的六种创建型模式。
工厂模式
工厂模式最终解决的是项目实例化问题。
这个单元作业中多项式的每一个因子,都是特定类型的函数(常数函数,幂函数,三角函数),我们可以建立因子类,用工厂模式来生成。对于因子类,我们希望它能便于因子类型的扩展,便于维护,于是设计一个因子族。对于最底层,是一个抽象函数,其中可以保存指数,系数,可以定义求导方法和输出方法。
因子族
1 import java.math.BigInteger; 2 3 public abstract class Factor { 4 protected BigInteger coefficient; //求导前的系数 5 protected BigInteger index; //求导前的指数 6 protected BigInteger deCoefficient; //求导后的系数 7 protected BigInteger deIndex; //求导后的指数 8 9 public abstract void derivative(); 10 public abstract void output(); 11 }
之后继承这个抽象类,生成具体的因子类。
幂函数因子
1 import java.math.BigInteger; 2 3 public class PowerFactor extends Factor{ 4 5 public PowerFactor(BigInteger coefficient, BigInteger index) { 6 this.coefficient = coefficient; 7 this.index = index; 8 } 9 10 @Override 11 public void derivative() { 12 deCoefficient = coefficient.multiply(index); 13 deIndex = index.subtract(BigInteger.ONE); 14 } 15 16 public String makeString() { 17 String string = null; 18 /* 19 * 此处略去若干行将求导结果转化成字符串的方法 20 */ 21 return string; 22 } 23 24 @Override 25 public void output() { 26 String string = makeString(); 27 System.out.println(string); 28 } 29 }
余弦函数类(正弦函数与幂函数类似)
1 import java.math.BigInteger; 2 3 public class CosFactor extends Factor{ 4 5 public CosFactor(BigInteger coefficient, BigInteger index) { 6 this.coefficient = coefficient; 7 this.index = index; 8 } 9 10 @Override 11 public void derivative() { 12 deCoefficient = BigInteger.ZERO.subtract(coefficient.multiply(index)); 13 deIndex = index.subtract(BigInteger.ONE); 14 } 15 16 public String makeString() { 17 String string = null; 18 /* 19 * 此处略去若干行将求导结果转化成字符串的方法 20 */ 21 return string; 22 } 23 24 @Override 25 public void output() { 26 String string = makeString(); 27 System.out.println(string); 28 } 29 }
设计好因子族后,我们需要建立一个选择因子的类,在这个类中获得要建造具体哪个对象的消息,然后实例化具体的对象。
1 public class ChooseFactor { 2 3 public ChooseFactor(String type, BigInteger coefficient, BigInteger index) { 4 Factor factor = null; 5 6 if (type.equals("power")) { 7 factor = new PowerFactor(coefficient, index); 8 } else if (type.equals("sin")) { 9 factor = new SinFactor(coefficient, index); 10 } else if (type.equals("cos")) { 11 factor = new CosFactor(coefficient, index); 12 } else { 13 System.out.println("WRONG FORMAT"); 14 System.exit(0); 15 } 16 factor.derivative(); 17 factor.output(); 18 } 19 }
这样的设计固然没错,但是这对以后的扩展不友好。当我们需要加入新的因子类,我们需要在 if…else 语句中新增一项。这要就违背了类设计的初衷,即类的设计是要做到对扩展的开放,对修改的关闭。
既然不想修改类内部的内容,我们可以将变化的部分和不变的部分各自分离开进行抽象和封装。我们分析一下这个类发现,其变化的部分是实例化对象时随着因子类型的增加或者减少,if…else 语句会有所改变。于是便有了简单工厂,它可以帮助我们解决这一问题。
简单工厂模式(Simple Factory)
对于工厂模式,最终解决的问题就是项目里面对象实例化的问题,要大量实例化某些类的对象,就要用到工厂模式。因为如果把这些实例化过程放在项目里面,它的变动、扩展、升级就会很麻烦,所以应将实例化放在某个区域一起批量管理
简单工厂设计方案:定义一个实例化因子对象的类,封装创建对象的代码。我们将上述分析出的变化的部分抽取出来放在简单工厂里面,剩下不变的东西留在ChooseFactor部分里面。
1 import java.math.BigInteger; 2 3 public class SimpleFactory { 4 5 public Factor CreateFactor(String type, BigInteger coefficient, BigInteger index) { 6 Factor factor = null; 7 if (type.equals("power")) { 8 factor = new PowerFactor(coefficient, index); 9 } else if (type.equals("sin")) { 10 factor = new SinFactor(coefficient, index); 11 } else if (type.equals("cos")) { 12 factor = new CosFactor(coefficient, index); 13 } else { 14 System.out.println("WRONG FORMAT"); 15 System.exit(0); 16 } 17 return factor; 18 } 19 }
简单工厂模式:定义一个创建对象的类,由这个类来封装实例化对象的行为。其实就是将原来实例化对象的语句抽象出来,新建一个类。
然后在原来的ChooseFactor类中,我们调用这个简单工厂。
1 public class ChooseFactor { 2 SimpleFactory simpleFactory; 3 4 public ChooseFactor(SimpleFactory simpleFactory, String type, BigInteger coefficient, BigInteger index) { 5 Factor factor = null; 6 this.simpleFactory = simpleFactory; 7 factor = simpleFactory.CreateFactor(type, coefficient, index); 8 factor.derivative(); 9 factor.output(); 10 } 11 }
但是问题又来了,这个ChooseFactor类我们最终输出的是一个字符串,但如果我们需要直接输出求导后的系数和指数怎么办呢?如果用简单工厂,我们就得建立一个生成String的工厂,再建立一个生成BigInteger的工厂,就实现上对这个项目来说问题不大,但就体系结构,可维护性和可扩展性来说对更大的项目是有问题的,所以就产生了工厂方法。
工厂方法模式(Factory Method)
对于工厂方法,我们建立一个抽象的选择因子的类,里面有抽象的创建因子方法。我们可以将它实例化成求不同阶导数的具体方法。
用工厂方法生成的ChooseFactor类,主体结构上与简单工厂类似。我们在具体产生因子的地方我们使用了一个createFactor的抽象方法。
1 public abstract class ChooseFactor { 2 3 public ChooseFactor(String type, BigInteger coefficient, BigInteger index) { 4 Factor factor = null; 5 factor = createFactor(type, coefficient, index); 6 factor.derivative(); 7 factor.output(); 8 } 9 10 public abstract Factor createFactor(String type, BigInteger coefficient, BigInteger index); 11 }
原先的幂函数因子输出的是字符串,为了区别我们将其重命名为StrPowerFactor,我们再建立一个输出指数和系数的幂函数因子,名为BigintPowerFactor。
1 import java.math.BigInteger; 2 3 public class BigintPowerFactor extends Factor{ 4 5 public BigintPowerFactor(BigInteger coefficient, BigInteger index) { 6 this.coefficient = coefficient; 7 this.index = index; 8 } 9 10 @Override 11 public void derivative() { 12 deCoefficient = coefficient.multiply(index); 13 deIndex = index.subtract(BigInteger.ONE); 14 } 15 16 @Override 17 public void output() { 18 System.out.print(deCoefficient.toString()); 19 System.out.print(" "); 20 System.out.println(deIndex.toString()); 21 } 22 }
对于输出字串,建立一个ChooseFactorStr。
1 import java.math.BigInteger; 2 3 public class ChooseFactorStr extends ChooseFactor{ 4 5 @Override 6 public Factor createFactor(String type, BigInteger coefficient, BigInteger index) { 7 Factor factor = null; 8 9 if (type.equals("power")) { 10 factor = new StrPowerFactor (coefficient, index); 11 } else if (type.equals("sin")) { 12 factor = new StrPowerFactor (coefficient, index); 13 } else if (type.equals("cos")) { 14 factor = new StrPowerFactor (coefficient, index); 15 } else { 16 System.out.println("WRONG FORMAT"); 17 System.exit(0); 18 } 19 return factor; 20 } 21 }
对于输出指数和系数,建立一个ChooseFactorBigint。
1 import java.math.BigInteger; 2 3 public class ChooseFactorBigint extends ChooseFactor{ 4 5 @Override 6 public Factor createFactor(String type, BigInteger coefficient, BigInteger index) { 7 Factor factor = null; 8 9 if (type.equals("power")) { 10 factor = new BigintPowerFactor (coefficient, index); 11 } else if (type.equals("sin")) { 12 factor = new BigintPowerFactor (coefficient, index); 13 } else if (type.equals("cos")) { 14 factor = new BigintPowerFactor (coefficient, index); 15 } else { 16 System.out.println("WRONG FORMAT"); 17 System.exit(0); 18 } 19 return factor; 20 } 21 }
最后,我们需要一个SelectFactory函数来选择调用哪一个工厂。
1 import java.math.BigInteger; 2 3 public class SelectFactory { 4 5 public SelectFactory(String type, BigInteger coefficient, BigInteger index) { 6 ChooseFactor chooseFactor = new ChooseFactorBigint(); 7 Factor factor = chooseFactor.createFactor(type, coefficient, index); 8 factor.derivative(); 9 factor.output(); 10 } 11 }
我们总结一下工厂方法模式。工厂方法模式:定义一个创建对象的抽象方法,由子类决定要实例化的类。也就是说工厂方法模式将对象的实例化推迟到子类。
这时我们又想到既然因子可以由因子族产生,为什么工厂不能呢?我们回忆简单工厂模式,它只能生成输出字符串的因子,如果想要生成输出BigInteger的因子,就得建立另一个简单工厂。而这些简单工厂又可以由一个工厂族产生,这就引出了抽象工厂模式。
抽象工厂模式(Abstract Factory)
抽象工厂模式:定义了一个接口用于创建相关或有依赖关系的对象族,而无需明确指定具体类。将原来进行对象实例化的代码抽取出来。
抽象工厂
1 import java.math.BigInteger; 2 3 public interface AbstractFactory { 4 public Factor CreateFactor(String type, BigInteger coefficient, BigInteger index); 5 }
输出字符串工厂
1 import java.math.BigInteger; 2 3 public class StringFactory implements AbstractFactory{ 4 5 public StringFactory() { 6 }; 7 8 @Override 9 public Factor CreateFactor(String type, BigInteger coefficient, BigInteger index) { 10 Factor factor = null; 11 if (type.equals("power")) { 12 factor = new StrPowerFactor (coefficient, index); 13 } else if (type.equals("sin")) { 14 factor = new StrSinFactor (coefficient, index); 15 } else if (type.equals("cos")) { 16 factor = new StrCosFactor (coefficient, index); 17 } else { 18 System.out.println("WRONG FORMAT"); 19 System.exit(0); 20 } 21 return factor; 22 } 23 }
输出BigInteger工厂
1 import java.math.BigInteger; 2 3 public class BigintFactory implements AbstractFactory{ 4 5 public BigintFactory () { 6 }; 7 8 @Override 9 public Factor CreateFactor(String type, BigInteger coefficient, BigInteger index) { 10 Factor factor = null; 11 if (type.equals("power")) { 12 factor = new BigintPowerFactor (coefficient, index); 13 } else if (type.equals("sin")) { 14 factor = new BigintSinFactor (coefficient, index); 15 } else if (type.equals("cos")) { 16 factor = new BigintCosFactor (coefficient, index); 17 } else { 18 System.out.println("WRONG FORMAT"); 19 System.exit(0); 20 } 21 return factor; 22 } 23 }
抽象工厂主要分为两级结构,第一级作为接口,将主要功能抽象出来。在ChooseFactor类中只用传入抽象工厂,具体是哪一个工厂是在选择工厂的函数里面确定的。
1 public class ChooseFactor { 2 AbstractFactory abstractFactory; 3 4 public ChooseFactor(AbstractFactory abstractFactory, String type, BigInteger coefficient, BigInteger index) { 5 Factor factor = null; 6 this.abstractFactory = abstractFactory; 7 factor = abstractFactory.CreateFactor(type, coefficient, index); 8 factor.derivative(); 9 factor.output(); 10 } 11 }
1 import java.math.BigInteger; 2 3 public class SelectFactory { 4 5 public SelectFactory(String type, BigInteger coefficient, BigInteger index) { 6 ChooseFactor chooseFactor = new ChooseFactor(new BigintFactory(), type, coefficient, index); 7 } 8 }
抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象。
观察这个SelectFactory函数,对于一个具体的工厂,如果我们每次调用ChooseFactor时都new一个新的,这样会占用很多不必要的空间,因为每个具体的工厂的功能都是一样的。这时我们就可以使用单例模式。
单例模式(Singleton)
单例模式:确保一个类最多只有一个实例,并提供一个全局访问点。为了做到这一点,我们将构造函数私有化,这样外界便无法创建实例,在内部调用产生唯一一个对象。
1 import java.math.BigInteger; 2 3 public class StringFactory implements AbstractFactory{ 4 5 private static StringFactory uniqueInstance = null; 6 7 private StringFactory() { 8 9 }; 10 11 public static StringFactory getInstance() { 12 if (uniqueInstance == null) { 13 uniqueInstance = new StringFactory(); 14 } 15 return uniqueInstance; 16 } 17 18 @Override 19 public Factor CreateFactor(String type, BigInteger coefficient, BigInteger index) { 20 Factor factor = null; 21 if (type.equals("power")) { 22 factor = new StrPowerFactor (coefficient, index); 23 } else if (type.equals("sin")) { 24 factor = new StrSinFactor (coefficient, index); 25 } else if (type.equals("cos")) { 26 factor = new StrCosFactor (coefficient, index); 27 } else { 28 System.out.println("WRONG FORMAT"); 29 System.exit(0); 30 } 31 return factor; 32 } 33 }
每次调用都只用调用这唯一存在的uniqueInstance。
1 import java.math.BigInteger; 2 3 public class StringFactory implements AbstractFactory{ 4 5 private static StringFactory uniqueInstance = null; 6 7 private StringFactory() { 8 9 }; 10 11 public static StringFactory getInstance() { 12 if (uniqueInstance == null) { 13 uniqueInstance = new StringFactory(); 14 } 15 return uniqueInstance; 16 } 17 18 @Override 19 public Factor CreateFactor(String type, BigInteger coefficient, BigInteger index) { 20 Factor factor = null; 21 if (type.equals("power")) { 22 factor = new StrPowerFactor (coefficient, index); 23 } else if (type.equals("sin")) { 24 factor = new StrSinFactor (coefficient, index); 25 } else if (type.equals("cos")) { 26 factor = new StrCosFactor (coefficient, index); 27 } else { 28 System.out.println("WRONG FORMAT"); 29 System.exit(0); 30 } 31 return factor; 32 } 33 }
在主函数里为了方便表示随机生成了系数和指数。
1 import java.math.BigInteger; 2 import java.util.Random; 3 4 public class Main { 5 6 public static String getNum(int lenth) { 7 String source = "0123456789"; 8 StringBuffer stringBuffer = new StringBuffer(); 9 Random random = new Random(); 10 for (int i = 0; i < lenth; i++) { 11 stringBuffer.append(source.charAt(random.nextInt(source.length()))); 12 } 13 return stringBuffer.toString(); 14 } 15 16 public static void main(String[] args) { 17 String type = "sin"; 18 BigInteger coefficient = new BigInteger(getNum(5)); 19 BigInteger index = new BigInteger(getNum(3)); 20 ChooseFactor chooseFactor = new ChooseFactor(StringFactory.getInstance(), type, coefficient, index); 21 } 22 }
其实对于StringFactory这个具体工厂,即使每次都new一个新的也不会出现错误。但是在之后有些项目中,有些对象例如线程池、缓存、硬件设备等等,它们只有一个。比如某个程序要使用打印机,而打印假脱机(printer spooler)只有一个,我如果建造了多个打印机对象,并且它们同时要使用打印机,就会产生冲突。所以,我们让类自身负责保存它唯一的实例,这个类保证没有其他实例可以被创建,这便是单例模式的好处所在。
原型模式(Prototype)
原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。也就是说,生产出一个包含大量公共信息的原型类,然后克隆出副本,修改细节信息,建立一个完整的个性对象。对于第二次作业,如果不考虑化简,那么输出的就是x、sin(x)和cos(x)三种,可以用原型模式。
首先建造一个输出的模板,这个模板里存放一些不会改变的信息(例如特定的字符串)。
1 public class OutputTemplate { 2 3 private String sinString = "*sin(x)^"; 4 private String cosString = "*cos(x)^"; 5 private String powerString = "*x^"; 6 7 public String getSinString() { 8 return sinString; 9 } 10 11 public String getCosString() { 12 return cosString; 13 } 14 15 public String getPowerString() { 16 return powerString; 17 } 18 }
之后创建一个项的类,实现一个克隆的接口,重写克隆对象的方法,返回一个克隆的对象,这个克隆对象拥有模板里的信息。
1 public class Term implements Cloneable { 2 3 public String getCoefficient() { 4 return coefficient; 5 } 6 7 public void setCoefficient(String coefficient) { 8 this.coefficient = coefficient; 9 } 10 11 public String getSinString() { 12 return sinString; 13 } 14 15 public void setSinString(String sinString) { 16 this.sinString = sinString; 17 } 18 19 public String getSinIndex() { 20 return sinIndex; 21 } 22 23 public void setSinIndex(String sinIndex) { 24 this.sinIndex = sinIndex; 25 } 26 27 public String getCosString() { 28 return cosString; 29 } 30 31 public void setCosString(String cosString) { 32 this.cosString = cosString; 33 } 34 35 public String getCosIndex() { 36 return cosIndex; 37 } 38 39 public void setCosIndex(String cosIndex) { 40 this.cosIndex = cosIndex; 41 } 42 43 public String getPowerString() { 44 return powerString; 45 } 46 47 public void setPowerString(String powerString) { 48 this.powerString = powerString; 49 } 50 51 public String getPowerIndex() { 52 return powerIndex; 53 } 54 55 public void setPowerIndex(String powerIndex) { 56 this.powerIndex = powerIndex; 57 } 58 59 private String coefficient; 60 private String sinString; 61 private String sinIndex; 62 private String cosString; 63 private String cosIndex; 64 private String powerString; 65 private String powerIndex; 66 67 public Term(OutputTemplate outputTemplate) { 68 this.sinString = outputTemplate.getSinString(); 69 this.cosString = outputTemplate.getCosString(); 70 this.powerString = outputTemplate.getPowerString(); 71 } 72 73 protected Object clone() throws CloneNotSupportedException { 74 Term term = null; 75 try { 76 term = (Term) super.clone(); 77 } catch (Exception e) { 78 e.printStackTrace(); 79 } 80 return term; 81 } 82 }
最后在主函数里修改每个项系数和指数并输出(这里用随机数生成系数和指数)。
1 import java.util.Random; 2 3 public class Test { 4 5 public static String getNum(int lenth) { 6 String source = "0123456789"; 7 StringBuffer stringBuffer = new StringBuffer(); 8 Random random = new Random(); 9 for (int i = 0; i < lenth; i++) { 10 stringBuffer.append(source.charAt(random.nextInt(source.length()))); 11 } 12 return stringBuffer.toString(); 13 } 14 15 public static void main(String[] args) { 16 Term term = new Term(new OutputTemplate()); 17 Term cloneTerm = null; 18 for(int i = 0; i < 5; i++) { 19 try { 20 cloneTerm = (Term) term.clone(); 21 } catch (CloneNotSupportedException e) { 22 e.printStackTrace(); 23 } 24 cloneTerm.setCoefficient(getNum(3)); 25 cloneTerm.setSinIndex(getNum(4)); 26 cloneTerm.setCosIndex(getNum(4)); 27 cloneTerm.setPowerIndex(getNum(5)); 28 System.out.println(cloneTerm.getCoefficient() + cloneTerm.getSinString() + cloneTerm.getSinIndex() + cloneTerm.getCosString() 29 + cloneTerm.getCosIndex() + cloneTerm.getPowerString() + cloneTerm.getPowerIndex()); 30 } 31 } 32 }
原型模式通过复制现有实例来创建新的对象,一般创建对象靠new,这个模式靠复制已有对象。好处是无需知道相应类的信息(构造函数等),而是直接内存上的复制,速度上也会更快。但类中不能出现final对象,Object类的克隆方法是浅拷贝,如果要实现深拷贝,必须另行拷贝。
原型模式在创建成本较大和希望目标对象的修改不影响既有原型对象时比较适用。
使用原型模式不会调用构造方法,所以与上述单例模式的理念是冲突的,不能同时使用。
生成器模式(Builder)
生成器模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
对于第二次作业的,虽然一项的输出只有x、sin(x)和cos(x)三种,但是却可以有系数和指数分别为0或1的十余种输出方式,这便可以采用生成器模式。下面,以这次输出为例,我们来看看生成器模式的各个部分。
首先建造一个因子模型的抽象类,其中有一系列保存在ArrayList里面的字符串,保存生成该模型的具体过程。
1 import java.math.BigInteger; 2 import java.util.ArrayList; 3 import java.util.List; 4 5 public abstract class FactorModel { 6 7 private BigInteger coefficient; 8 private BigInteger sinIndex; 9 private BigInteger cosIndex; 10 private BigInteger powerIndex; 11 private List<String> sequence = new ArrayList<>(); 12 13 protected abstract void printCoefficient(); 14 protected abstract void printSinFunction(); 15 protected abstract void printSinIndex(); 16 protected abstract void printCosFunction(); 17 protected abstract void printCosIndex(); 18 protected abstract void printPowerFunction(); 19 protected abstract void printPowerIndex(); 20 protected abstract void printMultipleSign(); 21 protected abstract void printNewLine(); 22 23 public final void print() { 24 for (int i = 0; i < sequence.size(); i++) { 25 String actionName = sequence.get(i); 26 if (actionName.equals("Coefficient")) { 27 this.printCoefficient(); 28 } else if (actionName.equals("SinFunction")) { 29 this.printSinFunction(); 30 } else if (actionName.equals("SinIndex")) { 31 this.printSinIndex(); 32 } else if (actionName.equals("CosFunction")) { 33 this.printCosFunction(); 34 } else if (actionName.equals("PowerFunction")) { 35 this.printPowerFunction(); 36 } else if (actionName.equals("PowerIndex")) { 37 this.printPowerIndex(); 38 } else if (actionName.equals("CosIndex")) { 39 this.printCosIndex(); 40 } else if (actionName.equals("MultipleSign")) { 41 this.printMultipleSign(); 42 } else if (actionName.equals("NewLine")) { 43 this.printNewLine(); 44 } 45 } 46 } 47 48 public final void setSequence(List<String> sequence) { 49 this.sequence = sequence; 50 } 51 52 public final void setCoefficient(BigInteger coefficient) { 53 this.coefficient = coefficient; 54 } 55 56 public final BigInteger getCoefficient() { 57 return coefficient; 58 } 59 60 public final void setSinIndex(BigInteger sinIndex) { 61 this.sinIndex = sinIndex; 62 } 63 64 public final BigInteger getSinIndex() { 65 return sinIndex; 66 } 67 68 public final void setCosIndex(BigInteger cosIndex) { 69 this.cosIndex = cosIndex; 70 } 71 72 public final BigInteger getCosIndex() { 73 return cosIndex; 74 } 75 76 public final void setPowerIndex(BigInteger powerIndex) { 77 this.powerIndex = powerIndex; 78 } 79 80 public final BigInteger getPowerIndex() { 81 return powerIndex; 82 } 83 }
我们可以由它生成一个具体模型TermModel,它便是第二次作业中的项(由且仅由sin(x)、cos(x)和x组成)。
1 import java.math.BigInteger; 2 3 public class TermModel extends FactorModel { 4 5 public TermModel(BigInteger coefficient, BigInteger sinIndex, BigInteger cosIndex, BigInteger powerIndex) { 6 setCoefficient(coefficient); 7 setSinIndex(sinIndex); 8 setCosIndex(cosIndex); 9 setPowerIndex(powerIndex); 10 } 11 12 @Override 13 protected void printCoefficient() { 14 BigInteger coefficient = getCoefficient(); 15 System.out.print(coefficient.toString()); 16 } 17 18 @Override 19 protected void printMultipleSign() { 20 System.out.print("*"); 21 } 22 23 @Override 24 protected void printSinFunction() { 25 System.out.print("sin(x)"); 26 } 27 28 @Override 29 protected void printSinIndex() { 30 BigInteger sinIndex = getSinIndex(); 31 System.out.print("^" + sinIndex); 32 } 33 34 @Override 35 protected void printCosFunction() { 36 System.out.print("cos(x)"); 37 } 38 39 @Override 40 protected void printCosIndex() { 41 BigInteger cosIndex = getCosIndex(); 42 System.out.print("^" + cosIndex); 43 } 44 45 @Override 46 protected void printNewLine() { 47 System.out.println(); 48 } 49 50 @Override 51 protected void printPowerFunction() { 52 System.out.print("x"); 53 } 54 55 @Override 56 protected void printPowerIndex() { 57 BigInteger powerIndex = getPowerIndex(); 58 System.out.print("^" + powerIndex); 59 } 60 }
之后我们建创建一个Builder的抽象类。
1 import java.util.List; 2 3 public abstract class Builder { 4 5 public abstract void setSequence(List<String> sequence); 6 public abstract TermModel getTermModel(); 7 }
由这个类生成一个项的生成器。
1 import java.math.BigInteger; 2 import java.util.List; 3 4 public class TermBuilder extends Builder{ 5 6 private TermModel termModel; 7 8 public TermBuilder(BigInteger coefficient, BigInteger sinIndex, BigInteger cosIndex, BigInteger powerIndex) { 9 termModel = new TermModel(coefficient, sinIndex, cosIndex, powerIndex); 10 } 11 12 @Override 13 public void setSequence(List<String> sequence) { 14 this.termModel.setSequence(sequence); 15 } 16 17 @Override 18 public TermModel getTermModel() { 19 return this.termModel; 20 } 21 22 }
之后我们需要一个总的Director来生成各种类型的项(这里只列举了三项)。
1 import java.math.BigInteger; 2 import java.util.ArrayList; 3 import java.util.List; 4 5 public class Director { 6 7 private List<String> sequence = new ArrayList<>(); 8 private TermBuilder termBuilder; 9 10 public void setTermBuilder(BigInteger coefficient, BigInteger sinIndex, BigInteger cosIndex, BigInteger powerIndex) { 11 termBuilder = new TermBuilder(coefficient, sinIndex, cosIndex, powerIndex); 12 } 13 14 public TermModel getTermModel1() { //没有系数 15 this.sequence.clear(); 16 this.sequence.add("SinFunction"); 17 this.sequence.add("SinIndex"); 18 this.sequence.add("MultipleSign"); 19 this.sequence.add("CosFunction"); 20 this.sequence.add("CosIndex"); 21 this.sequence.add("MultipleSign"); 22 this.sequence.add("PowerFunction"); 23 this.sequence.add("PowerIndex"); 24 this.sequence.add("NewLine"); 25 this.termBuilder.setSequence(sequence); 26 return (TermModel)termBuilder.getTermModel(); 27 } 28 29 public TermModel getTermModel2() { //sin(x)指数为1 30 this.sequence.clear(); 31 this.sequence.add("Coefficient"); 32 this.sequence.add("MultipleSign"); 33 this.sequence.add("SinFunction"); 34 this.sequence.add("MultipleSign"); 35 this.sequence.add("CosFunction"); 36 this.sequence.add("CosIndex"); 37 this.sequence.add("MultipleSign"); 38 this.sequence.add("PowerFunction"); 39 this.sequence.add("PowerIndex"); 40 this.sequence.add("NewLine"); 41 this.termBuilder.setSequence(sequence); 42 return (TermModel)termBuilder.getTermModel(); 43 } 44 45 public TermModel getTermModel3() { //没有cos(x)和x 46 this.sequence.clear(); 47 this.sequence.add("Coefficient"); 48 this.sequence.add("MultipleSign"); 49 this.sequence.add("SinFunction"); 50 this.sequence.add("SinIndex"); 51 this.sequence.add("NewLine"); 52 this.termBuilder.setSequence(sequence); 53 return (TermModel)termBuilder.getTermModel(); 54 } 55 }
最后在Main函数里面用随机数每个类生成了五个。
1 import java.math.BigInteger; 2 import java.util.Random; 3 4 public class Main { 5 6 public static String getNum() { 7 String source = "0123456789"; 8 StringBuffer stringBuffer = new StringBuffer(); 9 Random random = new Random(); 10 for(int i = 0; i < 5; i++) { 11 stringBuffer.append(source.charAt(random.nextInt(source.length()))); 12 } 13 return stringBuffer.toString(); 14 } 15 16 public static void main(String[] args) { 17 Director director = new Director(); 18 BigInteger coefficient; 19 BigInteger sinIndex; 20 BigInteger cosIndex; 21 BigInteger powerIndex; 22 System.out.println("没有系数类型"); 23 for (int i = 0; i < 5; i++) { 24 coefficient = new BigInteger(getNum()); 25 sinIndex = new BigInteger(getNum()); 26 cosIndex = new BigInteger(getNum()); 27 powerIndex = new BigInteger(getNum()); 28 director.setTermBuilder(coefficient, sinIndex, cosIndex, powerIndex); 29 director.getTermModel1().print(); 30 } 31 System.out.println("\nsin(x)指数为1类型"); 32 for (int i = 0; i < 5; i++) { 33 coefficient = new BigInteger(getNum()); 34 sinIndex = new BigInteger(getNum()); 35 cosIndex = new BigInteger(getNum()); 36 powerIndex = new BigInteger(getNum()); 37 director.setTermBuilder(coefficient, sinIndex, cosIndex, powerIndex); 38 director.getTermModel2().print(); 39 } 40 System.out.println("\n没有cos(x)类型"); 41 for (int i = 0; i < 5; i++) { 42 coefficient = new BigInteger(getNum()); 43 sinIndex = new BigInteger(getNum()); 44 cosIndex = new BigInteger(getNum()); 45 powerIndex = new BigInteger(getNum()); 46 director.setTermBuilder(coefficient, sinIndex, cosIndex, powerIndex); 47 director.getTermModel3().print(); 48 } 49 } 50 }
生成器模式,顾名思义应该是用于建立某个对象,之前所述的工厂模式也是用来生成对象的,那么生成器模式与工厂模式有什么区别呢?生成器模式主要用于复杂对象的生成,复杂对象指类里面属性较多,结构较复杂的对象等等。
其优点是将复杂的对象的创建过程封装起来,允许对象通过几个步骤来创建,可以改变其过程(工厂模式通常只有一个步骤),只需指定特定的生成器就能生成特定对象,隐藏类的内部结构。
抽象工厂与生成器相似,主要区别是生成器模式着重于一步一步构造一个复杂对象,而抽象工厂着重于多个系列的产品对象(简单的或是复杂的)。生成器在最后一步返回产品,而对于抽象工厂来说,产品是立即返回的。
以上就是对六种创建型模式的分析,很多地方是为了借求导来说明设计模式,使运算变得复杂了。如果有不对的地方,还请批评指正。
基于度量的代码分析
第一次作业——输入输出的处理
第一次作业我没能很好地完成从面向过程到面向对象的转变,一共只设计了两个类,因为每个项都只有幂函数,所以可以很方便地用正则表达式来匹配每一项,于是用正则表达式将输入裁剪成项逐个处理,最终输出只要合并同类项和将正项放在第一个,就能拿到性能分满分。对于将正项放在第一个这一步骤,我是用一次遍历找到最大数放在第一个这样完成的。
第二次作业
第二次作业加入了三角函数部分,因为没有因子的嵌套,我们发现每一项都可以用系数、sin(x)指数、cos(x)指数和x指数来唯一确定,这就对合并同类项这一步骤产证了很大的帮助。之后再对可以化简的三角部分进行化简,我在这篇博客里有很详细的叙述,在此不再赘述。
第三次作业
第三次作业用了递归来求,这个程序几乎没有保存任何输入数据,都是输入、求导后直接输出,这使得化简变得比较困难,所以我只化简了输出的“*0项“”和“1*”因子,性能分没有得到。这次的思路就与第一次不同了,因为用正则表达式来匹配一个可以无限嵌套的因子可以说是不可能的,因此我采用的方式是:用“+”将整个表达式分成项,在项内用“*”将其分解成因子,在因子中匹配“x”、“sin(x)”、“cos(x)”和嵌套因子,匹配到“x”、“sin(x)”和“cos(x)”时终止递归。
BUG分析
这三次作业强侧均未出现bug,第一次互测出现了未考虑空格的一个同质bug,第二次互测出现了乘号出现在某一项末尾的情况没有考虑,第三次互测无bug。