最新公告
  • 欢迎您光临欧资源网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入我们
  • 使用java8特性是如何另辟蹊径解决的问题的(组图)

    新的语言特性通常会掩盖现有的编程模式或设计。例如,Java 5 中引入了 for-each 循环,由于其健壮性和简单性,它已经取代了许多显式使用迭代器的情况。Java 7中引入的菱形运算符()在创建实例时不需要显式使用泛型,这在一定程度上促进了Java程序员使用类型接口进行编程。

    设计经验的概括称为设计模式。在设计软件时,如果你愿意,你可以重用这些方法来解决一些常见的问题。这看起来像传统建筑工程师的工作方式,为典型场景定义了可重用的解决方案。例如,访问者模式通常用于将程序的算法与其操作对象分开。单例模式一般用于限制类的实例化,只生成一个对象。

    Lambda 表达式为程序员的工具箱添加了另一个强大的工具。不仅如此,它们为传统设计模式面临的问题提供了新的解决方案,而且它们通常更高效、更易于采用。使用 lambda 表达式后,许多现有的略显臃肿的面向对象设计模式可以以更紧凑的方式实现。

    本文简要介绍了如何使用 java8 特性作为一种新的方式来解决设计模式原本试图解决的问题。

    1、策略模式

    策略模式代表了一类算法的通用解决方案,可以在运行时选择。这种模式可以应用于更广泛的领域,例如使用不同的标准验证输入,以不同的方式解析或格式化输入。

    策略模式由三部分组成:

    假设您要验证您的输入是否根据标准正确格式化(例如只有小写字母或数字)。

    1、1 传统方式

    您可以首先定义一个验证文本的接口(以字符串的形式):

    public interface ValidationStrategy {
        boolean execute(String s);
    }

    其次,定义接口的一个或多个具体实现:

    public class IsAllLowerCase implements ValidationStrategy {
        public boolean execute(String s){
            return s.matches("[a-z]+");
        }
    }
    public class IsNumeric implements ValidationStrategy {
        public boolean execute(String s){
            return s.matches("\d+");
        }
    }

    之后,您可以在程序中使用这些略有不同的验证策略:

    public class Validator{
        private final ValidationStrategy strategy;
        public Validator(ValidationStrategy v){
            this.strategy = v;
        }
        public boolean validate(String s){
            return strategy.execute(s);
        }
    }
    Validator numericValidator = new Validator(new IsNumeric());
    boolean b1 = numericValidator.validate("aaaa");
    Validator lowerCaseValidator = new Validator(new IsAllLowerCase ());
    boolean b2 = lowerCaseValidator.validate("bbbb");

    1、2 JAVA8方式

    使用java8后,可以看出ValidationStrategy是一个函数式接口,它的功能描述也和Predicate一样。这意味着我们不需要声明一个新的类来实现不同的策略,我们可以通过直接传递 lambda 表达式来实现同样的事情,而且更简洁:

    Validator numericValidator = new Validator((String s) -> s.matches("[a-z]+"));
    boolean b1 = numericValidator.validate("aaaa");
    Validator lowerCaseValidator =new Validator((String s) -> s.matches("\d+"));
    boolean b2 = lowerCaseValidator.validate("bbbb");

    如上所述,Lambda 表达式在采用策略设计模式时避免了死板的模板代码。Lambda 表达式实际上已经封装了部分代码(或策略),这就是创建策略设计模式的初衷。因此,强烈建议尽可能使用 Lambda 表达式来解决类似的问题。

    2、 模板设计模式

    如果你需要使用一个算法的框架,同时又希望有一定的灵活性,可以对它的某些部分进行改进,那么模板方法设计模式是一个更通用的解决方案。模板方法模式在“我想使用这个算法,但它的某些行需要改进以达到预期的效果”时很有用。

    假设您需要编写一个简单的网上银行应用程序。通常,用户需要输入一个用户账户,然后应用程序才能从银行的数据库中获取用户的详细信息,最后完成一些让用户满意的操作。不同网点的网银应用程序满足客户的方式也可能略有不同,例如向客户的账户发放奖金或只是发送较少的促销文件。通过下面的抽象类方法

    实施网上银行应用程序:

    2、1 传统方式

    public abstract class OnlineBanking {
        public void processCustomer(int id){
            Customer c = Database.getCustomerWithId(id);
            makeCustomerHappy(c);
        }
        abstract void makeCustomerHappy(Customer c);
    }

    processCustomer 方法构建了网上银行算法的框架:获取客户提供的ID,然后提供满足用户的服务。不同的分行可以通过继承OnlineBanking类来提供该方法的差异化实现。

    2、2 JAVA8方式

    这些问题也可以使用 lambda 表达式来解决(创建算法框架并在某些部分插入具体的实现)。您想要插入的不同算法组件可以通过 lambda 表达式或方法引用来实现。这里我们给processCustomer方法引入第二个参数,它是Consumer类型的参数,和上面定义的makeCustomerHappy的特性是一致的:

    public void processCustomer(int id, Consumer makeCustomerHappy){
        Customer c = Database.getCustomerWithId(id);
        makeCustomerHappy.accept(c);
    }

    现在,很容易通过传递 lambda 表达式直接插入不同的行为,而不是从 OnlineBanking 类继承:

    new OnlineBankingLambda().processCustomer(1337, (Customer c) ->System.out.println("Hello " + c.getName());

    这是 Lamba 表达式如何解决设计模式固有的设计刚性的又一个例子。

    3、 观察者模式

    当某个对象(通常称为主体)需要在发生某些事件(例如状态转换)时自动通知多个其他对象(称为观察者)时,使用此方案。这种设计模式经常在创建图形用户界面 (GUI) 程序时使用。在这种情况下,在 GUI 组件(如按钮)上注册了一系列观察者。如果单击该按钮,观察者将收到通知并执行特定操作。但是观察者模式并不局限于 GUI。例如,观察者设计模式也适用于股票交易的情况,其中多个经纪人可能希望对某只股票的价格(主题)变化做出反应。

    假设您需要为 Twitter 等应用程序设计和实现自定义通知系统。纽约时报、卫报和世界报等多家报纸机构订阅新闻并希望在收到的新闻包含他们感兴趣的关键字时得到通知。

    3、1 传统方式

    首先,需要一个观察者接口,它将不同的观察者聚合在一起。它只有一个名为 notify 的方法,一旦收到一条新消息就会调用该方法:

    public interface Observer {
        void notify(String tweet);
    }

    声明不同的观察者(例如这里是三个不同的报社)c语言 图形用户界面,根据新闻中不同的关键词定义不同的行为:

    class NYTimes implements Observer{
        public void notify(String tweet) {
            if(tweet != null && tweet.contains("money")){
                System.out.println("Breaking news in NY! " + tweet);
            }
        }
    }
    class Guardian implements Observer{
        public void notify(String tweet) {
            if(tweet != null && tweet.contains("queen")){
                System.out.println("Yet another news in London... " + tweet);
            }
        }
    }
    class LeMonde implements Observer{
        public void notify(String tweet) {
            if(tweet != null && tweet.contains("wine")){
                System.out.println("Today cheese, wine and news! " + tweet);
            }
        }
    }

    定义一个主题接口:

    interface Subject{
        void registerObserver(Observer o);
        void notifyObservers(String tweet);
    }

    Subject 可以使用 registerObserver 方法注册一个新的观察者,并使用 notifyObservers 方法通知它的观察者有消息到达。让我们更进一步,实现 Feed 类:

    class Feed implements Subject{
        private final List observers = new ArrayList();
        public void registerObserver(Observer o) {
            this.observers.add(o);
        }
        public void notifyObservers(String tweet) {
            observers.forEach(o -> o.notify(tweet));
        }
    }

    Feed 类在内部维护一个观察者列表,当新闻到达时通知:

    Feed f = new Feed();
    f.registerObserver(new NYTimes());
    f.registerObserver(new Guardian());
    f.registerObserver(new LeMonde());
    f.notifyObservers("The queen said her favourite person is Steven!");

    卫报会关注这个消息!

    3、2 JAVA8方式

    Observer 接口的所有实现类都提供了一种方法:notify。当消息到达时,它们都只是包装了同一段代码的执行。Lambda 表达式旨在消除这种死板的代码。使用 lambda 表达式后,不需要显式实例化三个观察者对象,可以直接传递 lambda 表达式来表达要执行的行为:

    f.registerObserver((String tweet) -> {
        if(tweet != null && tweet.contains("money")){
            System.out.println("Breaking news in NY! " + tweet);
        }
    });
    f.registerObserver((String tweet) -> {
        if(tweet != null && tweet.contains("queen")){
            System.out.println("Yet another news in London... " + tweet);
        }
    });

    那么,我们可以随时随地使用 Lambda 表达式吗?答案是否定的!在上面描述的示例中,Lambda 非常适合c语言 图形用户界面,因为要执行的操作很简单,因此很容易消除刚性代码。然而,观察者的逻辑可能相当复杂,他们也可能持有状态,或者定义多个方法等等。在这些情况下,您应该继续使用类方法。

    4 责任链模型

    责任链模式是一种用于创建一系列处理对象(例如一系列操作)的通用方案。处理程序对象可能需要做一些工作,将结果传递给另一个对象,然后在将结果传递给下一个处理程序对象之前做一些工作,依此类推。通常,这种模式是通过定义一个表示处理对象的抽象类来实现的,其中定义了一个字段来记录后续对象。一旦对象完成其工作,处置对象将其工作传递给其继任者。

    4、1 传统方式

    public abstract class ProcessingObject {
        protected ProcessingObject successor;
        public void setSuccessor(ProcessingObject successor){
            this.successor = successor;
        }
        public T handle(T input){
            T r = handleWork(input);
            if(successor != null){
                return successor.handle(r);
            }
            return r;
        }
        abstract protected T handleWork(T input);
    }

    创建两个处理对象,其功能是做一些文本处理:

    public class HeaderTextProcessing extends ProcessingObject {
        public String handleWork(String text){
            return "There is a person: " + text;
        }
    }
    public class SpellCheckerProcessing extends ProcessingObject {
        public String handleWork(String text){
            return text.replaceAll("Steven", "niu");
        }
    }

    现在你可以结合这两个处理对象来构造一个操作序列!

    ProcessingObject p1 = new HeaderTextProcessing();
    ProcessingObject p2 = new SpellCheckerProcessing();
    p1.setSuccessor(p2);
    String result = p1.handle("Steven is niubility persion.");

    4、2Java8方式

    这种模式看起来像链接(即构造)函数,允许处理对象成为函数的实例,或者更确切地说是 UnaryOperator 的实例。为了链接这些函数,需要使用 andThen 方法来构造它们。

    UnaryOperator headerProcessing = (String text) -> "There is a persion: " + text;
    UnaryOperator spellCheckerProcessing = (String text) -> text.replaceAll("Steven", "niu");
    Function pipeline = headerProcessing.andThen(spellCheckerProcessing);
    String result = pipeline.apply("Steven is niubility persion");

    5、工厂模式

    使用工厂模式,可以在不向客户端公开实例化逻辑的情况下完成对象创建。假设为一家银行工作,他们需要一种方法来创建不同的金融产品:贷款、期权、股票等。通常,创建一个工厂类,其中包含一个负责实现不同对象的方法

    5、1 传统方式

    public class ProductFactory {
        public static Product createProduct(String name){
        switch(name){
            case "loan": return new Loan();
            case "stock": return new Stock();
            case "bond": return new Bond();
            default: throw new RuntimeException("No such product " + name);
            }
        }
    }

    在这里,贷款、股票和债券都是产品的子类。createProduct 方法可以为每个创建的产品设置额外的逻辑。但好处也很明显

    看,您不必担心在创建对象时向客户端公开构造函数或配置,这使客户端更容易创建产品:

    Product p = ProductFactory.createProduct("loan");

    5、2Java8方式

    java8之后,构造函数可以像方法一样被引用。例如,下面是引用贷款构造函数的示例:

    Supplier loanSupplier = Loan::new;
    Loan loan = loanSupplier.get();

    这样,可以重构前面的代码,创建一个将产品名称映射到相应构造函数的 Map:

    final static Map<String, Supplier> map = new HashMap();
    static {
        map.put("loan", Loan::new);
        map.put("stock", Stock::new);
        map.put("bond", Bond::new);
    }

    您现在可以使用此 Map 来实例化不同的产品,就像您之前使用工厂设计模式所做的那样。

    public static Product createProduct(String name){
        Supplier p = map.get(name);
        if(p != null){
            return p.get();
        } 
        throw new IllegalArgumentException("No such product " + name);
    }

    6、总结

    Lambda 表达式有助于避免在使用面向对象设计模式时容易出现的死板的模板代码,而函数式编程专门实践了声明式编程(“只需使用互不影响的表达式,描述你想做的事情,并由系统来选择如何实现它”)和无副作用计算,这两个想法使构建和维护系统变得更容易。

    站内大部分资源收集于网络,若侵犯了您的合法权益,请联系我们删除!
    欧资源网 » 使用java8特性是如何另辟蹊径解决的问题的(组图)

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    欧资源网
    一个高级程序员模板开发平台

    发表评论