精华内容
下载资源
问答
  • Scene 情景假设情景一:银行账户的快速判断;假设需要判断某银行用户的其中一账号(profileA),币种(Currency)为人民币(CNY),余额是否大于1,0000,然后进行某业务逻辑处理。情景二:验证文件的扩展名是否合格。...

    Scene 情景假设

    情景一:银行账户的快速判断;

    假设需要判断某银行用户的其中一个账号(profileA),币种(Currency)为人民币(CNY),余额是否大于1,0000,然后进行某业务逻辑处理。

    情景二:验证文件的扩展名是否合格。

    Abstract 概述

    为了进行场景1的判断,需要判断/验证该账号是否为profileA、币种是否为CNY,余额是否大于1,0000这3种情况,在代码中可能会写出一大堆if...else语句,不仅不雅观,还不便于理解。如果使用一个整数int来同时标识多个判断的结果,就可以避免一大堆if...else的情况。当然这个例子只是作为demo说明情况,具体怎么处理还得根据实际出发,而且要是需要作更多判断,这个方法就会更实用。

    Description 详细说明

    原理分析

    对于计算机来说,所有数据都是由二进制数来表示,对于每一位(bit)来说,有0或1这2种情况,分别对应的boolean值为false和true。在Java中,每个int的大小为4 byte(注意是基本类型的int,而不是引用类型的Integer),一共有4*8=32位(bit),每一个位可对应一个boolean值的话,那么1个int最多可以同时对应32个boolean值。

    具体步骤

    大概可分为3步:

    定义每个二进制位数上的含义(即标准),并在代码中以十六进制的形式表示;(其实十进制也可以,只是在十六进制下其实就是1/2/4/8这几个数字在不同的位上循环,比十进制数要直观,可以在下图感受一下)

    将多个待验证的条件通过逻辑与(OR, "|")组合起来(token);

    根据第一点的标准,计算出当前值(code);

    若要判断满足token的所有条件,将当前值和token进行逻辑与(AND, "&")运算,所得结果再与token比较;

    return (mod & modifier) == modifier;

    若要判断满足token的至少一个条件,将当前值和token进行逻辑与(AND, "&")运算,所得结果再与0比较;

    return (mod & modifier) != 0;

    JDK中的实现:java.lang.reflect.Modifier

    在java.lang.reflect.Modifier中定义了变量的修饰符(modifier,即staic/final/public/private等);

    在Modifier类中并没有进行多条件的组合,但我们可以在自定义的类中进行组合;

    public static final int PUBLIC_STATIC_FINAL = PUBLIC | STATIC | FINAL;

    将要验证的编码(整数)传入,与modifier进行逻辑与(AND, "&")运算,再根据具体情况判断;

    return (mod & PUBLIC) != 0;

    return (mod & PUBLIC_STATIC_FINAL) == PUBLIC_STATIC_FINAL;

    代码中:

    public class Modifier {

    public static final int PUBLIC = 0x00000001;

    public static final int PRIVATE = 0x00000002;

    public static final int PROTECTED = 0x00000004;

    public static final int STATIC = 0x00000008;

    public static final int FINAL = 0x00000010;

    public static final int SYNCHRONIZED = 0x00000020;

    public static final int VOLATILE = 0x00000040;

    public static final int TRANSIENT = 0x00000080;

    }

    转换成表格:

    java.lang.reflect.Modifier

    二进制

    十进制

    十六进制

    public

    0000,0001

    2^0= 1

    0x00000001

    private

    0000,0010

    2^1=2

    0x00000002

    protected

    0000,0100

    2^2=4

    0x00000004

    static

    0000,1000

    2^3=8

    0x00000008

    final

    0001,0000

    2^4=16

    0x00000010

    synchronized

    0010,0000

    2^5=32

    0x00000020

    volatile

    0100,0000

    2^6=64

    0x00000040

    transient

    1000,0000

    2^7=128

    0x00000080

    验证:主要看System.out.println();打印输出的几行即可,其他都是辅助理解。

    package com.scv.lawrence.multicompare;

    import java.lang.reflect.Field;

    import java.lang.reflect.Modifier;

    import static com.scv.lawrence.multicompare.DemoTools.STATIC_FINAL;

    import static com.scv.lawrence.multicompare.DemoTools.compare;

    //为了减少篇幅,实际开发中个人不推荐使用静态导入

    public class FirstStep {

    /* 定义了3个不同修饰符组合的变量(static、final),并在代码中验证 */

    private static int age = 999; //age → static

    private static final String name = "Lawrence"; // name → static + final

    public final String gender = "Male"; //gender → final

    public static void main(String[] args) throws Exception {

    Field myAge = FirstStep.class.getDeclaredField("age");

    int ageMod = myAge.getModifiers();

    System.out.println("age的modifier(十进制): " + ageMod);

    System.out.println("\t" + "是否包含static和final:" + compare(ageMod, STATIC_FINAL));

    System.out.println("\t" + "是否包含static:" + compare(ageMod, Modifier.STATIC));

    Field myName = FirstStep.class.getDeclaredField("name");

    int nameMod = myName.getModifiers();

    System.out.println("************");

    System.out.println("name的modifier(十进制): " + nameMod);

    System.out.println("\t" + "是否包含static和final:" + compare(nameMod, STATIC_FINAL));

    System.out.println("\t" + "是否包含static:" + compare(nameMod, Modifier.STATIC));

    Field myGender = FirstStep.class.getDeclaredField("gender");

    int genderMod = myGender.getModifiers();

    System.out.println("************");

    System.out.println("gender的modifier(十进制): " + genderMod);

    System.out.println("\t" + "是否包含static和final:" + compare(genderMod, STATIC_FINAL));

    System.out.println("\t" + "是否包含static:" + compare(genderMod, Modifier.STATIC));

    }

    }

    /* output:

    *

    * age的modifier(十进制): 10

    * 是否包含static和final:false

    * 是否包含static:true

    * ************

    * name的modifier(十进制): 26

    * 是否包含static和final:true

    * 是否包含static:true

    * ************

    * gender的modifier(十进制): 17

    * 是否包含static和final:false

    * 是否包含static:false

    */

    Example 场景应用

    场景一 银行账户

    自定义Profile类,在实例化的时候就计算并保存其类型;

    在ToolBox类中定义各种标识的含义;

    在main()中对profile进行验证并执行相应业务逻辑;

    package com.scv.lawrence.multicompare;

    public class ThirdStep {

    public static void main(String[] args) {

    Profile[] profiles = {new Profile("ABC0001", "CNY", 12_0000),

    new Profile("SRC0001", "CNY", 21_0000),//target profile

    new Profile("ZZZ666GRE", "USD", 9000)};

    for(Profile p: profiles){

    if(ToolBox.isTargetProfile(p, ToolBox.PRO_A_CNY_GT)){

    //Do something.

    System.out.println("Got target profile.");

    };

    }

    }

    }

    class ToolBox{

    public static final int PROFILE_A = 0x0000_0001;//0001

    public static final int CNY = 0x0000_0002;//0010

    public static final int GT_10K = 0x0000_0004;//0100

    public static final int LT_10K = 0x0000_0008;//1000

    public static final int PRO_A_CNY_GT = PROFILE_A | CNY | GT_10K;//0111,profileA、币种为人民币、余额大于1,0000

    public static final int PRO_A_CNY_LT = PROFILE_A | CNY | LT_10K;//1111,profileA、币种为人民币、余额不足1,0000

    protected static void setToken(Profile p){

    if("SRC0001".equals(p.getName())) p.token += 0x0000_0001;

    if("CNY".equals(p.getCurrency())) p.token += 0x0000_0002;

    if(p.getBalance() > 1_0000) p.token += 0x0000_0004;

    }

    public static boolean isTargetProfile(Profile p, int mod){

    return (p.getToken() & mod) == mod;

    }

    }

    class Profile{

    private String name;

    private String currency;

    private double balance;

    protected int token;

    public int getToken(){

    return this.token;

    }

    public Profile() {}

    public Profile(String name, String currency, double balance){

    this.name = name;

    this.currency = currency;

    this.balance = balance;

    ToolBox.setToken(this);

    }

    public String getName() {

    return name;

    }

    public void setName(String name) {

    this.name = name;

    }

    public String getCurrency() {

    return currency;

    }

    public void setCurrency(String currency) {

    this.currency = currency;

    }

    public double getBalance() {

    return balance;

    }

    public void setBalance(double balance) {

    this.balance = balance;

    };

    }

    如需扩展,在ToolBox定义新的内容即可添加新的业务即可。

    p.s:数字下划线是JDK 1.7的语法糖,仅有方便(人类)阅读的作用,编译时会自动去掉。如1_0000_0000、100_000_000、100000000这几个数字在编译后都一样,但极大地方便了阅读。

    场景二 验证多个文件扩展名

    值得注意的是,由于是判断是否满足一个或多个条件,因此将判断表达式改为return (code & RULER) != 0;

    package com.scv.lawrence.multicompare;

    public class SecondStep {

    private static String[] fileNames = {"picA.jpg", "picB.jpg", "picC.png", "picD.gif", "picE.jpeg", "picF.abc"};

    public static void main(String[] args) {

    checkSuffix(fileNames);

    }

    public static boolean checkSuffix(String[] fileNames){

    boolean flag = true;

    for(String s : fileNames){

    int c = s.lastIndexOf(".");

    if(c != -1){

    String suffix = s.substring(c + 1).toLowerCase();

    int code = getCode(suffix); //获取文件扩展名并判断

    flag = Ruler.isAllow(code);

    System.out.println(s + ": " + (flag ? "pass" : "not pass"));

    if(!flag) return false; //只要有一个不合格的格式就直接返回false

    }else{

    return false; //没有扩展名,直接返回false

    }

    }

    return flag;

    }

    public static int getCode(String suf){

    int code = 0;

    switch(suf){

    case "jpg": //switch语句使用字符串作判断条件也是jdk1.7的特性,其实质仍然是int(编译后计算hashCode)

    code = Ruler.JPG;

    break;

    case "jpeg":

    code = Ruler.JPEG;

    break;

    case "png":

    code = Ruler.PNG;

    break;

    case "gif":

    code = Ruler.GIF;

    break;

    default:

    code = Ruler.DEFAULT;

    break;

    }

    return code;

    }

    }

    class Ruler{

    public static final int DEFAULT = 0x0;

    public static final int JPG = 0x1;

    public static final int JPEG = 0x2;

    public static final int PNG = 0x4;

    public static final int GIF = 0x8;

    public static final int RULER = JPG | JPEG | PNG | GIF;

    //只允许jpg、jpeg、png、gif格式的文件

    public static boolean isAllow(int code){

    return (code & RULER) != 0;

    }

    }

    Summary 总结

    优点

    避免大量的if...else判断;

    易于扩展维护代码;

    二进制的计算,高效;

    缺点

    需要先定义作比较的条件和计算出具体值,占了一定的代码量;

    初次接触的话可能不易理解;

    与switch的比较

    轻松进行多个条件的判断,且可以进行子集条件的判断,即对于满足A|B|C|D的条件,也会同时满足A|B、B|D或B|C|D等等。switch并不具备这样的能力。

    Reference 参考

    展开全文
  • 本篇文章给大家带来的内容是关于总结Java代码书写的四个技巧 ,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。我们平时的编程任务不外乎就是将相同的技术套件应用到不同的项目中去,对于大多数...

    本篇文章给大家带来的内容是关于总结Java代码书写的四个技巧 ,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

    我们平时的编程任务不外乎就是将相同的技术套件应用到不同的项目中去,对于大多数情况来说,这些技术都是可以满足目标的。然而,有的项目可能需要用到一些特别的技术,因此工程师们得深入研究,去寻找那些最简单但最有效的方法。本文我们将介绍一些有助于解决常见问题的通用设计策略和目标实现技术,即:只做有目的性的优化

    常量尽量使用枚举

    重新定义类里面的equals()方法

    尽量多使用多态性

    值得注意的是,本文中描述的技术并不是适用于所有情况。另外这些技术应该什么时候使用以及在什么地方使用,都是需要使用者经过深思熟虑的。

    1 .只做有目的性的优化

    大型软件系统肯定非常关注性能问题。虽然我们希望能够写出最高效的代码,但很多时候,如果想对代码进行优化,我们却无从下手。例如,下面的这段代码会影响到性能吗?public void processIntegers(List integers) {

    for (Integer value: integers) {

    for (int i = integers.size() - 1; i >= 0; i--) {

    value += integers.get(i);

    }

    }

    }

    这就得视情况而定了。上面这段代码可以看出它的处理算法是O(n³)(使用大O符号),其中n是list集合的大小。如果n只有5,那么就不会有问题,只会执行25次迭代。但如果n是10万,那可能会影响性能了。请注意,即使这样我们也不能判定肯定会有问题。尽管此方法需要执行10亿次逻辑迭代,但会不会对性能产生影响仍然有待讨论。

    例如,假设客户端是在它自己的线程中执行这段代码,并且异步等待计算完成,那么它的执行时间有可能是可以接受的。同样,如果系统部署在了生产环境上,但是没有客户端进行调用,那我们根本没必要去对这段代码进行优化,因为压根就不会消耗系统的整体性能。事实上,优化性能以后系统会变得更加复杂,悲剧的是系统的性能却没有因此而提高。

    最重要的是天下没有免费的午餐,因此为了降低代价,我们通常会通过类似于缓存、循环展开或预计算值这类技术去实现优化,这样反而增加了系统的复杂性,也降低了代码的可读性。如果这种优化可以提高系统的性能,那么即使变得复杂,那也是值得的,但是做决定之前,必须首先知道这两条信息:性能要求是什么

    性能瓶颈在哪里

    首先我们需要清楚地知道性能要求是什么。如果最终是在要求以内,并且最终用户也没有提出什么异议,那么就没有必要进行性能优化。但是,当添加了新功能或者系统的数据量达到一定规模以后就必须进行优化了,否则可能会出现问题。

    在这种情况下,不应该靠直觉,也不应该依靠检查。因为即使是像Martin Fowler这样有经验的开发人员也容易做一些错误的优化,正如在重构(第70页)一文中解释的那样:如果分析了足够多的程序以后,你会发现关于性能的有趣之处在于,大部分时间都浪费在了系统中的一小部分代码中里面。如果对所有代码进行了同样的优化,那么最终结果就是浪费了90%的优化,因为优化过以后的代码运行得频率并不多。因为没有目标而做的优化所耗费的时间,都是在浪费时间。

    作为一名身经百战的开发人员,我们应该认真对待这一观点。第一次猜测不仅没有提高系统的性能,而且90%的开发时间完全是浪费了。相反,我们应该在生产环境(或者预生产环境中)执行常见用例,并找出在执行过程中是哪部分在消耗系统资源,然后对系统进行配置。例如消耗大部分资源的代码只占了10%,那么优化其余90%的代码就是浪费时间。

    根据分析结果,要想使用这些知识,我们应该从最常见的情况入手。因为这将确保实际付出的努力最终是可以提高系统的性能。每次优化后,都应该重复分析步骤。因为这不仅可以确保系统的性能真的得到了改善,也可以看出再对系统进行优化后,性能瓶颈是在哪个部分(因为解决完一个瓶颈以后,其它瓶颈可能消耗系统更多的整体资源)。需要注意的是,在现有瓶颈中花费的时间百分比很可能会增加,因为剩下的瓶颈是暂时不变的,而且随着目标瓶颈的消除,整个执行时间应该会减少。

    尽管在Java系统中想要对概要文件进行全面检查需要很大的容量,但是还是有一些很常见的工具可以帮助发现系统的性能热点,这些工具包括JMeter、AppDynamics和YourKit。另外,还可以参见DZone的性能监测指南,获取更多关于Java程序性能优化的信息。

    虽然性能是许多大型软件系统一个非常重要的组成部分,也成为产品交付管道中自动化测试套件的一部分,但是还是不能够盲目的且没有目的的进行优化。相反,应该对已经掌握的性能瓶颈进行特定的优化。这不仅可以帮助我们避免增加了系统的复杂性,而且还让我们少走弯路,不去做那些浪费时间的优化。

    2.常量尽量使用枚举

    需要用户列出一组预定义或常量值的场景有很多,例如在web应用程序中可能遇到的HTTP响应代码。最常见的实现技术之一是新建类,该类里面有很多静态的final类型的值,每个值都应该有一句注释,描述该值的含义是什么:public class HttpResponseCodes {

    public static final int OK = 200;

    public static final int NOT_FOUND = 404;

    public static final int FORBIDDEN = 403;

    }

    if (getHttpResponse().getStatusCode() == HttpResponseCodes.OK) {

    // Do something if the response code is OK

    }

    能够有这种思路就已经非常好了,但这还是有一些缺点:

    没有对传入的整数值进行严格的校验

    由于是基本数据类型,因此不能调用状态代码上的方法

    在第一种情况下只是简单的创建了一个特定的常量来表示特殊的整数值,但并没有对方法或变量进行限制,因此使用的值可能会超出定义的范围。例如:public class HttpResponseHandler {

    public static void printMessage(int statusCode) {

    System.out.println("Recieved status of " + statusCode);

    }

    }

    HttpResponseHandler.printMessage(15000);

    尽管15000并不是有效的HTTP响应代码,但是由于服务器端也没有限制客户端必须提供有效的整数。在第二种情况下,我们没有办法为状态代码定义方法。例如,如果想要检查给定的状态代码是否是一个成功的代码,那就必须定义一个单独的函数:public class HttpResponseCodes {

    public static final int OK = 200;

    public static final int NOT_FOUND = 404;

    public static final int FORBIDDEN = 403;

    public static boolean isSuccess(int statusCode) {

    return statusCode >= 200 && statusCode < 300;

    }

    }

    if (HttpResponseCodes.isSuccess(getHttpResponse().getStatusCode())) {

    // Do something if the response code is a success code

    }

    为了解决这些问题,我们需要将常量类型从基本数据类型改为自定义类型,并只允许自定义类的特定对象。这正是Java枚举(enum)的用途。使用enum,我们可以一次性解决这两个问题:public enum HttpResponseCodes {

    OK(200),

    FORBIDDEN(403),

    NOT_FOUND(404);

    private final int code;

    HttpResponseCodes(int code) {

    this.code = code;

    }

    public int getCode() {

    return code;

    }

    public boolean isSuccess() {

    return code >= 200 && code < 300;

    }

    }

    if (getHttpResponse().getStatusCode().isSuccess()) {

    // Do something if the response code is a success code

    }

    同样,现在还可以要求在调用方法的时候提供必须有效的状态代码:public class HttpResponseHandler {

    public static void printMessage(HttpResponseCode statusCode) {

    System.out.println("Recieved status of " + statusCode.getCode());

    }

    }

    HttpResponseHandler.printMessage(HttpResponseCode.OK);

    值得注意的是,举这个例子事项说明如果是常量,则应该尽量使用枚举,但并不是说什么情况下都应该使用枚举。在某些情况下,可能希望使用一个常量来表示某个特殊值,但是也允许提供其它的值。例如,大家可能都知道圆周率,我们可以用一个常量来捕获这个值(并重用它):public class NumericConstants {

    public static final double PI = 3.14;

    public static final double UNIT_CIRCLE_AREA = PI * PI;

    }

    public class Rug {

    private final double area;

    public class Run(double area) {

    this.area = area;

    }

    public double getCost() {

    return area * 2;

    }

    }

    // Create a carpet that is 4 feet in diameter (radius of 2 feet)

    Rug fourFootRug = new Rug(2 * NumericConstants.UNIT_CIRCLE_AREA);

    因此,使用枚举的规则可以归纳为:

    当所有可能的离散值都已经提前知道了,那么就可以使用枚举

    再拿上文中所提到的HTTP响应代码为例,我们可能知道HTTP状态代码的所有值(可以在RFC 7231中找的到,它定义了HTTP 1.1协议)。因此使用了枚举。在计算圆周率的情况下,我们不知道关于圆周率的所有可能值(任何可能的double都是有效的),但同时又希望为圆形的rugs创建一个常量,使计算更容易(更容易阅读);因此定义了一系列常量。

    如果不能提前知道所有可能的值,但是又希望包含每个值的字段或方法,那么最简单的方法就是可以新建一个类来表示数据。尽管没有说过什么场景应该绝对不用枚举,但要想知道在什么地方、什么时间不使用枚举的关键是提前意识到所有的值,并且禁止使用其他任何值。

    3.重新定义类里面的equals()方法

    对象识别可能是一个很难解决的问题:如果两个对象在内存中占据相同的位置,那么它们是相同的吗?如果它们的id相同,它们是相同的吗?或者如果所有的字段都相等呢?虽然每个类都有自己的标识逻辑,但是在系统中有很多西方都需要去判断是否相等。例如,有如下的一个类,表示订单购买…public class Purchase {

    private long id;

    public long getId() {

    return id;

    }

    public void setId(long id) {

    this.id = id;

    }

    }

    ……就像下面写的这样,代码中肯定有很多地方都是类似于的:Purchase originalPurchase = new Purchase();

    Purchase updatedPurchase = new Purchase();

    if (originalPurchase.getId() == updatedPurchase.getId()) {

    // Execute some logic for equal purchases

    }

    这些逻辑调用的越多(反过来,违背了DRY原则),Purchase类的身份信息也会变得越来越多。如果出于某种原因,更改了Purchase类的身份逻辑(例如,更改了标识符的类型),则需要更新标识逻辑所在的位置肯定也非常多。

    我们应该在类的内部初始化这个逻辑,而不是通过系统将Purchase类的身份逻辑进行过多的传播。乍一看,我们可以创建一个新的方法,比如isSame,这个方法的入参是一个Purchase对象,并对每个对象的id进行比较,看看它们是否相同:public class Purchase {

    private long id;

    public boolean isSame(Purchase other) {

    return getId() == other.gerId();

    }

    }

    虽然这是一个有效的解决方案,但是忽略了Java的内置功能:使用equals方法。Java中的每个类都是继承了Object类,虽然是隐式的,因此同样也就继承了equals方法。默认情况下,此方法将检查对象标识(内存中相同的对象),如JDK中的对象类定义(version 1.8.0_131)中的以下代码片段所示:public boolean equals(Object obj) {

    return (this == obj);

    }

    这个equals方法充当了注入身份逻辑的自然位置(通过覆盖默认的equals实现):public class Purchase {

    private long id;

    public long getId() {

    return id;

    }

    public void setId(long id) {

    this.id = id;

    }

    @Override

    public boolean equals(Object other) {

    if (this == other) {

    return true;

    }

    else if (!(other instanceof Purchase)) {

    return false;

    }

    else {

    return ((Purchase) other).getId() == getId();

    }

    }

    }

    虽然这个equals方法看起来很复杂,但由于equals方法只接受类型对象的参数,所以我们只需要考虑三个案例:

    另一个对象是当前对象(即originalPurchase.equals(originalPurchase)),根据定义,它们是同一个对象,因此返回true

    另一个对象不是Purchase对象,在这种情况下,我们无法比较Purchase的id,因此,这两个对象不相等

    其他对象不是同一个对象,但却是Purchase的实例,因此,是否相等取决于当前Purchase的id和其他Purchase是否相等

    现在可以重构我们之前的条件,如下:Purchase originalPurchase = new Purchase();

    Purchase updatedPurchase = new Purchase();

    if (originalPurchase.equals(updatedPurchase)) {

    // Execute some logic for equal purchases

    }

    除了可以在系统中减少复制,重构默认的equals方法还有一些其它的优势。例如,如果构造一个Purchase对象列表,并检查列表是否包含具有相同ID(内存中不同对象)的另一个Purchase对象,那么我们就会得到true值,因为这两个值被认为是相等的:List purchases = new ArrayList<>();

    purchases.add(originalPurchase);

    purchases.contains(updatedPurchase); // True

    通常,无论在什么地方,如果需要判断两个类是否相等,则只需要使用重写过的equals方法就可以了。如果希望使用由于继承了Object对象而隐式具有的equals方法去判断相等性,我们还可以使用= =操作符,如下:if (originalPurchase == updatedPurchase) {

    // The two objects are the same objects in memory

    }

    还需要注意的是,当equals方法被重写以后,hashCode方法也应该被重写。有关这两种方法之间关系的更多信息,以及如何正确定义hashCode方法,请参见此线程。

    正如我们所看到的,重写equals方法不仅可以将身份逻辑在类的内部进行初始化,并在整个系统中减少了这种逻辑的扩散,它还允许Java语言对类做出有根据的决定。

    4.尽量多使用多态性

    对于任何一门编程语言来说,条件句都是一种很常见的结构,而且它的存在也是有一定原因的。因为不同的组合可以允许用户根据给定值或对象的瞬时状态改变系统的行为。假设用户需要计算各银行账户的余额,那么就可以开发出以下的代码:public enum BankAccountType {

    CHECKING,

    SAVINGS,

    CERTIFICATE_OF_DEPOSIT;

    }

    public class BankAccount {

    private final BankAccountType type;

    public BankAccount(BankAccountType type) {

    this.type = type;

    }

    public double getInterestRate() {

    switch(type) {

    case CHECKING:

    return 0.03; // 3%

    case SAVINGS:

    return 0.04; // 4%

    case CERTIFICATE_OF_DEPOSIT:

    return 0.05; // 5%

    default:

    throw new UnsupportedOperationException();

    }

    }

    public boolean supportsDeposits() {

    switch(type) {

    case CHECKING:

    return true;

    case SAVINGS:

    return true;

    case CERTIFICATE_OF_DEPOSIT:

    return false;

    default:

    throw new UnsupportedOperationException();

    }

    }

    }

    虽然上面这段代码满足了基本的要求,但是有个很明显的缺陷:用户只是根据给定帐户的类型决定系统的行为。这不仅要求用户每次要做决定之前都需要检查账户类型,还需要在做出决定时重复这个逻辑。例如,在上面的设计中,用户必须在两种方法都进行检查才可以。这就可能会出现失控的情况,特别是接收到添加新帐户类型的需求时。

    我们可以使用多态来隐式地做出决策,而不是使用账户类型用来区分。为了做到这一点,我们将BankAccount的具体类转换成一个接口,并将决策过程传入一系列具体的类,这些类代表了每种类型的银行帐户:public interface BankAccount {

    public double getInterestRate();

    public boolean supportsDeposits();

    }

    public class CheckingAccount implements BankAccount {

    @Override

    public double getIntestRate() {

    return 0.03;

    }

    @Override

    public boolean supportsDeposits() {

    return true;

    }

    }

    public class SavingsAccount implements BankAccount {

    @Override

    public double getIntestRate() {

    return 0.04;

    }

    @Override

    public boolean supportsDeposits() {

    return true;

    }

    }

    public class CertificateOfDepositAccount implements BankAccount {

    @Override

    public double getIntestRate() {

    return 0.05;

    }

    @Override

    public boolean supportsDeposits() {

    return false;

    }

    }

    这不仅将每个帐户特有的信息封装到了到自己的类中,而且还支持用户可以在两种重要的方式中对设计进行变化。首先,如果想要添加一个新的银行帐户类型,只需创建一个新的具体类,实现了BankAccount的接口,给出两个方法的具体实现就可以了。在条件结构设计中,我们必须在枚举中添加一个新值,在两个方法中添加新的case语句,并在每个case语句下插入新帐户的逻辑。

    其次,如果我们希望在BankAccount接口中添加一个新方法,我们只需在每个具体类中添加新方法。在条件设计中,我们必须复制现有的switch语句并将其添加到我们的新方法中。此外,我们还必须在每个case语句中添加每个帐户类型的逻辑。

    在数学上,当我们创建一个新方法或添加一个新类型时,我们必须在多态和条件设计中做出相同数量的逻辑更改。例如,如果我们在多态设计中添加一个新方法,我们必须将新方法添加到所有n个银行帐户的具体类中,而在条件设计中,我们必须在我们的新方法中添加n个新的case语句。如果我们在多态设计中添加一个新的account类型,我们必须在BankAccount接口中实现所有的m数,而在条件设计中,我们必须向每个m现有方法添加一个新的case语句。

    虽然我们必须做的改变的数量是相等的,但变化的性质却是完全不同的。在多态设计中,如果我们添加一个新的帐户类型并且忘记包含一个方法,编译器会抛出一个错误,因为我们没有在我们的BankAccount接口中实现所有的方法。在条件设计中,没有这样的检查,以确保每个类型都有一个case语句。如果添加了新类型,我们可以简单地忘记更新每个switch语句。这个问题越严重,我们就越重复我们的switch语句。我们是人类,我们倾向于犯错误。因此,任何时候,只要我们可以依赖编译器来提醒我们错误,我们就应该这么做。

    关于这两种设计的第二个重要注意事项是它们在外部是等同的。例如,如果我们想要检查一个支票帐户的利率,条件设计就会类似如下:BankAccount checkingAccount = new BankAccount(BankAccountType.CHECKING);

    System.out.println(checkingAccount.getInterestRate()); // Output: 0.03

    相反,多态设计将类似如下:BankAccount checkingAccount = new CheckingAccount();

    System.out.println(checkingAccount.getInterestRate()); // Output: 0.03

    从外部的角度来看,我们只是在BankAccount对象上调用getintereUNK()。如果我们将创建过程抽象为一个工厂类的话,这将更加明显:public class ConditionalAccountFactory {

    public static BankAccount createCheckingAccount() {

    return new BankAccount(BankAccountType.CHECKING);

    }

    }

    public class PolymorphicAccountFactory {

    public static BankAccount createCheckingAccount() {

    return new CheckingAccount();

    }

    }

    // In both cases, we create the accounts using a factory

    BankAccount conditionalCheckingAccount = ConditionalAccountFactory.createCheckingAccount();

    BankAccount polymorphicCheckingAccount = PolymorphicAccountFactory.createCheckingAccount();

    // In both cases, the call to obtain the interest rate is the same

    System.out.println(conditionalCheckingAccount.getInterestRate()); // Output: 0.03

    System.out.println(polymorphicCheckingAccount.getInterestRate()); // Output: 0.03

    将条件逻辑替换成多态类是非常常见的,因此已经发布了将条件语句重构为多态类的方法。这里就有一个简单的例子。此外,马丁·福勒(Martin Fowler)的《重构》(p . 255)也描述了执行这个重构的详细过程。

    就像本文中的其他技术一样,对于何时执行从条件逻辑转换到多态类,没有硬性规定。事实上,如论在何种情况下我们都是不建议使用。在测试驱动的设计中:例如,Kent Beck设计了一个简单的货币系统,目的是使用多态类,但发现这使设计过于复杂,于是便将他的设计重新设计成一个非多态风格。经验和合理的判断将决定何时是将条件代码转换为多态代码的合适时间。

    结束语

    作为程序员,尽管平常所使用的常规技术可以解决大部分的问题,但有时我们应该打破这种常规,主动需求一些创新。毕竟作为一名开发人员,扩展自己知识面的的广度和深度,不仅能让我们做出更明智的决定,也能让我们变得越来越聪明。

    相关推荐:

    展开全文
  • 但是能够对win10系统巧用cmd命令快速创建新账户进行实际操作的人却不。其实解决win10系统巧用cmd命令快速创建新账户的问题也不是难事,小编这里提示两点:1、首先大家依次点击联想笔记本win10系统的“开始”——...

    有关win10系统巧用cmd命令快速创建新账户的操作方法想必大家有所耳闻。但是能够对win10系统巧用cmd命令快速创建新账户进行实际操作的人却不多。其实解决win10系统巧用cmd命令快速创建新账户的问题也不是难事,小编这里提示两点:1、首先大家依次点击联想笔记本win10系统的“开始”——“搜索”,接着在搜索框里面我们输入“CMd”指令按回车; 2、其次大家在结果里面使用鼠标右击搜索到的“CMd”程序,并且点选“以管理员的身份运行”;;掌握这些就够了。如果还想了解更多详细内容,可继续学习下面关于win10系统巧用cmd命令快速创建新账户的操作方法的介绍。

    06d4df28475dee1741ae52fddca58502.png

    1、首先大家依次点击联想笔记本win10系统的“开始”——“搜索”,接着在搜索框里面我们输入“CMd”指令按回车;

    49b7da63163c3beadbfd119bde97ada7.png

    2、其次大家在结果里面使用鼠标右击搜索到的“CMd”程序,并且点选“以管理员的身份运行”;

    3、然后在“命令行”的菜单里面直接填进“ net user /? ”命令,其次执行查看该命令;

    c1521033555eb122debd18bd694a96c3.png

    4、我们可以运用“net user”命令进行添加新用户的操作(比方:net user testuser mima /add 可以帮我们添加名为“testuser”的用户, 它的密码则是mima);

    5、其次我们要为进行创立的新用户添加它的管理员的权限,运行“.net localgroup administrators testuser /add”命令;

    6、紧跟着我们把新建的“testuser”用户加进“administrators”的组里面;

    7、最后大家就能够往“控制面板”里看见,刚才我们在“命令行”下新创建的“testuser”用户了。

    以上就是联想笔记本win10系统下巧用cmd命令快速创建新账户的方法,感兴趣的朋友们可以依照上面的方法步骤进行操作,这样就可以快速创建新账户了,是不是很简单呢,更多精彩内容欢迎继续关注win10之家!

    展开全文
  • 删除电脑中多余的用户的4技巧2020年09月02日|萬仟网科技 |我要评论删除电脑中多余的用户的4技巧技巧1 XP系统的创建了两用户但是有一给忘记密码了,... 09-05-18技巧1xp系统的创建了两用户但是有一给...

    删除电脑中多余的用户的4个小技巧

    2020年09月02日

    | 萬仟网科技

     | a48e1d17a2b0511faa7da7240b1971f6.png我要评论

    删除电脑中多余的用户的4个小技巧技巧1 XP系统的创建了两个用户但是有一个给忘记密码了,怎样才能在不知道密码的情况下把他给删除呢!~ 初重做系统外~!!!在两个都是管理员的情况下我怎么能删除掉啊. 要删除... 09-05-18

    技巧1

    xp系统的创建了两个用户但是有一个给忘记密码了,怎样才能在不知道密码的情况下把他给删除呢!~

    初重做系统外~!!!在两个都是管理员的情况下我怎么能删除掉啊.

    要删除说什么还需要磁盘的~!!~!~

    最佳答案:依次点击“开始”-“运行”,输入net user 你要删除的用户名 /delete 假如你要删除的用户名是123,则你输入 net user 123 /delete

    技巧2

    重新启动--f8--进安全模式--控制面板--管理工具--计算机管理--本地用户和组--用户--右边窗口--删除不要的用户!--重启动

    技巧3

    如果是administrator不能删除,但是你可以用administrator登陆安全模式,禁用他。如果你的系统是xp pro版本,也可以进控制面版->管理工具->本地安全策略->本地策略->安全选项->重命名系统管理员帐户,把他改成你想用的名字

    如果是非administrator帐户。。你可以在控制面版->管理工具->计算机管理->系统工具->本地用户和组->用户 里面删除你想删的帐户就可以了

    以上操作的前提是你当前帐户所属的组为系统管理员

    技巧4(最简便)

    开始--运行--lusrmgr.msc--回车--用户--自己看着办 (可以按提示要求去做)

    版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。

    如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

    相关文章:

    gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg

    百度地图中怎么标注旅游路线图?百度地图中怎么标注旅游路线图?百度地图中想要将自己要去旅游的离线先用红线标注出来看,该怎么标注呢?下面我们就来看看详细的教程,需...

    gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg

    支付宝怎么删除交易记录?支付宝清除交易记录的方法支付宝怎么删除交易记录?支付宝的交易记录太多了,不想都显示出来,很透露隐私,该怎么办呢?我们可以将不想显示的记...

    gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg

    搜狗输入法输入字数统计在哪 搜狗输入法字数统计排名怎么看搜狗输入法输入字数统计是什么?在哪里?今天小编就为大家带来搜狗输入法输入字数统计在哪以及搜狗输入法字数...

    gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg

    360安全卫士怎么设置默认浏览器如果电脑中有多个浏览器想要设置其中一个为默认浏览器,其实不用再设置中苦苦寻找设置位置。我们使用360卫士就能实现了。360安全...

    网友评论

    387afa1e91d68b43eca6c11dc07bf577.gif

    验证码:

    661155.html

    展开全文
  • 新手用户在使用Mac时要创建账户,在Mac上可以创建多个账户,Mac账户一般可分为4个等级,而根据账户的级别不同权限也不同,如何添加、更改账户信息?下面我们分享一下Mac新手要掌握的Mac账户如何操作。 Mac账户...
  • 原标题:系统小技巧:Windows 10账户删除 禁用与启用Windows 10可以支持多个本地账户。当我们暂时不用某个账户时,是删除账户还是禁用账户?二者有何区别?禁用后如果要启用,又该怎么做?1. 禁用账户≠删除账户禁用...
  • 所以小编特别为大家搜寻了10方法帮助大家的WIN10专业版系统进行加速!让WIN10专业版系统进行加速10方法:1、磁盘碎片整理硬盘使用时间久了,加上不断的文件存放、删除,会导致硬盘文件不连续存放,这样使用硬盘...
  • 配置计算机接受这些连接并不复杂,不过,通过互联网为同一网络上的多个计算机配置远程访问并不那么简单。下面,我们就一步一步地配置,最终实现通过互联网到达计算机的多个远程连接。对于每一台要远程控制的计算机来...
  • 写出优质Java代码的4个技巧我们平时的编程任务不外乎就是将相同的技术套件应用到不同的项目中去,对于大多数情况来说,这些技术都是可以满足目标的。然而,有的项目可能需要用到一些特别的技术,因此工程师们得深入...
  • 我主页中的思维导图中内容大多从我的笔记中整理而来,相应技巧可在笔记中查找原题, 有兴趣的可以去 我的主页了解更计算机学科的精品思维导图整理 本文可以转载,但请注明来处,觉得整理的不错的小伙伴可以点赞关注...
  • 针式打印机的页面打印设置小技巧解析针式打印机受进纸方式制约,进纸时,都要卷入一小段,固定纸张,这一小段是无法用“页面设置”设置的。同样,打印完后,下边距也要留一小段,同样也无法用“页面设置”设置。即使...
  • 因为一个Apple ID可以被用于由苹果提供的多个产品和服务,它也可以称为Apple账户、MobileMe账户、Mac账户、iTunes Store账户和iChat账户。 苹果账户如此重要,那么如何保护 Apple ID的安全呢?以下就是小编带来的...
  • 在苹果系统中,我们有时候需要对系统进行一些设置,那么就可以通过系统偏好设置来操作,不过对于一些刚使用苹果...3、如果你还是找不到的,那么按图一两箭头指向的快捷键,cmmand+space,然后弹出的搜索框,点击它。
  • 线程小技巧

    2021-04-16 04:37:21
    当编写线程时 使用time模块使线程阻塞,查看阻塞时是否会导致数据变脏 import time time.sleep(1) 如一张饭卡上面至有50筷,分别在两刷卡点扣款30 import threading import time #定义一张饭卡帐户的类 class ...
  • 初始化一仓库并上传到 github 上 git init 初始化本地仓库 git add . 添加全部已经修改的文件,准备commit 提交 git commit -m “first commit” 将修改后的文件提交到本地仓库 git remote add origin ...
  • 但是能够对win10系统让新建账户直接覆盖administrator账户进行实际操作的人却不。其实解决win10系统让新建账户直接覆盖administrator账户的问题也不是难事,小编这里提示两点:1、首先我们按照顺序依次点开“开始...
  • 建立与删除普通用户账户,管理组 管理帐户的俱行工具及功能如下: useradd [] 添加用户 usermod [] 修改已存在的指定用户 userdel [-r] 删除已存在的指定帐户,-r参数用于删除用户自家目录 groupadd [] 加组 ...
  • 原标题:系统小技巧:Windows 10账户删除 禁用与启用Windows 10可以支持多个本地账户。当我们暂时不用某个账户时,是删除账户还是禁用账户?二者有何区别?禁用后如果要启用,又该怎么做?1. 禁用账户≠删除账户禁用...
  • Ø微信“切换帐户”功能Ø微信应用程序的双重功能Ø计算机上有多个微信登录从6.6.2版本开始,微信具有[切换帐户]功能,对于拥有两个微信帐户的朋友来说非常方便. 要切换帐户时,无需注销,只需单击[设置]-[切换帐户]...
  • 本文收集了一堆的shell脚本技巧,我说过,我写博客主要是作一些学习笔记,方便自己查阅,所以,我会搞出这么一篇文章,也没有什么不可理解的。关于这些技巧的出处,诶,我也忘了,可能来自theunixschool、 ...
  • 因为学习中要用到跨域请求,所以不得不在wamp集成环境下添加站点服务。1、首先你要确保已经正确安装了wamp。2、接着在wamp的安装目录下找到Apache2的httpd.conf文件,比如我的是在C:\wamp\bin\apache\Apache2.2.21...
  • 如果说信息流就是花钱买流量,那账户结构就是有效控制流量的工具。...一精细化的百度信息流账户结构决定了我们账户成本的高与低,它包含推广计划、推广单元、创意三基本元素。前期账户搭建对后期数据分析至关重...
  • 但在搜索技术上百度没有Google那么强大,很多人觉得Google搜索东西难找,其实这与Google对国内用户的体验不是很了解以及大家对Google搜索技巧不是很熟悉,下面电脑百事网编辑为大家整理了100多个Google搜索技巧,有...
  • 相信入手Mac的小白们往往都忽略了 访达Finder 这又熟悉又觉得陌生的Mac原生APP,其实熟练后也会大大增加工作效率 。 它的图标就是这蓝白相间的笑脸! Finder 使用技巧 1、访达Finder 到底是什么? 在MacOS...
  • 73GitHub高级搜索技巧

    千次阅读 多人点赞 2021-07-07 12:03:37
    73GitHub高级搜索技巧1. 前言2. 排序搜索结果2.1. 按交互排序2.2. 按反应排序2.3. 按作者日期排序2.4. 按提交者日期排序2.5. 按更新日期排序3. 搜索范围3.1. 搜索存储库3.1.1. 按存储库名称、描述或 README 文件的...
  • 原标题:10不为人知的SQL技巧策划 | 蔡芳芳作者 | Lukas Eder编译 | 刘雅梦编辑 | Linda导读:从早期开始,编程语言设计者就有这样的愿望:设计一种语言,在这种语言中,告诉机器我们想要的结果是什么(WHAT),而...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 31,680
精华内容 12,672
关键字:

多个账户打新技巧