In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-26 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
This article mainly introduces what is the function of Traits in Scala. It is very detailed and has a certain reference value. Friends who are interested must finish it!
Traits introduction
Before we get into object-oriented programming, we need to understand one more feature of Scala: Traits. It takes a little historical knowledge to understand this function.
In Java, a class can implement any number of interfaces. This model is useful when declaring that a class implements multiple abstractions. Unfortunately, it also has a major drawback.
For many interfaces, most functionality can be implemented with "boilerplate" code that is valid for all classes that use this interface. Java does not provide a built-in mechanism to define and use this reusable code. Instead, Java programmers must use a special transformation to reuse the implementation of a known interface. In the worst case, the programmer must copy and paste the same code into different classes.
Typically, an implementation of an interface has members that are independent (orthogonal) of other members of the instance. The term mixin is often used to refer to dedicated, potentially reusable, and independently maintainable code in an instance.
Take a look at the following code for the graphical user interface button, which uses a callback for the click event.
/ / code-examples/Traits/ui/button-callbacks.scala package ui class ButtonWithCallbacks (val label: String, val clickedCallbacks: List [() = > Unit]) extends Widget {require (clickedCallbacks! = null, "Callback list can't be null!") Def this (label: String, clickedCallback: () = > Unit) = this (label, List (clickedCallback)) def this (label: String) = {this (label, Nil) println ("Warning: button has no click callbacks!")} def click () = {/ /. Logic to give the appearance of clicking a physical button... ClickedCallbacks.foreach (f = > f ())}}
There's a lot going on here. The main constructor takes a label (label) argument and a callbacks (callback) list (list), which are called when the button's click method is called. We will explore more details of this class in Chapter 5, basic object-oriented programming for Scala. Now, we want to focus on a particular issue. ButtonWithCallbacks not only handles some of the essential behavior of buttons (such as clicks), it also handles notification of click events by calling callback functions. This violates the single-duty principle [Martin2003], which is a design method of separating functions. We can separate the logic of the button class from the callback logic so that each logical component becomes simpler, more modular, and more reusable. This callback logic is a good example of mixin.
Such separation is difficult in Java, and even if we define an interface with callback behavior, we still need to integrate implementation code into the class to reduce modularity. The only other way is to use specific tools such as aspect-oriented programming (Aspect-Oriented Programming,AOP, see [AOSD]), and one implementation is an extension of AspectJ,Java. AOP is mainly designed to separate the implementation of common problems that occur repeatedly in applications. It tries to modularize these key points, but also allows for a "mix" of well-designed and other key point behavior, including the core domain logic of the application, whether at compile time or run time.
Traits as a mixture
Scala provides a complete mixin solution called Traits. In our example, we can define the callback abstraction as a Trait, just like a Java interface, but we can implement these Trait (or inherited Trait) abstractions. We can define classes that are mixed with Trait, which is roughly like an interface that implements Java. However, in Scala you can even mix Traits when we create an instance. That is, we don't have to declare a class that mixes all the Trait we need first. So Scala Traits keeps the separation keys while giving us the ability to integrate behavior on demand.
If you come from the Java programming world, you can think of Traits as a selectively implemented interface. Other languages provide Trait-like structures, such as modules in Ruby.
Let's use Trait for the logic of the button to separate the processing of callbacks. We will promote our implementation. The callback is actually a special case of the observer mode [GOF1995]. So let's create a Trait to implement this pattern and then use it to handle the callback behavior. For simplicity, let's start with a separate callback that calculates the number of times the button has been pressed.
First, let's define a simple Button class.
/ / code-examples/Traits/ui/button.scala package ui class Button (val label: String) extends Widget {def click () = {/ / Logic to give the appearance of clicking a button... }}
Here is its parent class, Widget.
/ / code-examples/Traits/ui/widget.scala package ui abstract class Widget
The logic for managing callbacks (for example, the clickedCallbacks list) is omitted, as are the two main constructors. Only the button's label field and click method are preserved. This click method is now only concerned with the visible performance of a "physical" button when clicked. The only thing a button cares about is dealing with its essential behavior as a button.
Here is a Trait that implements the logic of the observer pattern.
/ code-examples/Traits/observer/observer.scala package observer Trait Subject {type Observer = {def receiveUpdate (subject: Any)} private var observers = List [Observer] () def addObserver (observer:Observer) = observers:: = observer def notifyObservers = observers foreach (_ .receiveUpdate (this))}
Except for the Trait keyword, Subject looks like a normal class. Subject defines all the members it declares. Traits can declare abstract members, concrete members, or both, just as classes can do (see the "overriding classes and members of Traits" section in Chapter 6-Scala Advanced object-oriented programming for more information). Furthermore, Traits can contain nested Trait and class definitions, and classes can contain nested Trait definitions.
The first line defines the Observer type. This is a structural type in the form {def receiveUpdate (subject:Any)}. Structure types specify only the structures that subtypes must support; you can think of them as "anonymous" types.
In this example, the structure type is defined by a method with a specific signature. Any type of method with this signature can be used as an observer. We will learn more about structural types in Chapter 12, the Scala Type system. If you want to know why we don't take Subject as a parameter, we take Any instead. We will review this problem in the "self-type annotations and abstract type members" section of Chapter 13-Application Design.
The most important thing we need to pay attention to is how this type of structure minimizes the coupling between Subject Trait and any potential Trait users.
Be careful
Subject is still coupled to the method name in Observer by structure type, for example, a method named receiveUpdate. We have several ways to save the rest of the coupling. We'll see how to do this in the "overriding Abstract types" section in Chapter 6-Scala Advanced object-oriented programming.
Next, we declare a series of observers. We define a var, not a val, because List is immutable. So we have to create a new list when an observer is added through the addObserver method.
We will discuss more details about List in Chapter 7-Scala object system, "Scala Type structure" and "Chapter 8-Scala functional programming." Now, notice that addObserver uses the list's cons "operator" method (::) to add an observer in front of a list. The Scala compiler will cleverly put the following statement
Observers:: = observer
Convert to the following statement
Observerobservers = observer:: observers
Notice that we wrote observer:: observers and put the list of existing observers to the right. Recall that all methods that end with: are right-bound. Therefore, the previous statement is equivalent to the following statement.
Observersobservers = observers.:: (observer)
The notifyObservers method iterates through all the observers, using the foreach method, and then calls the receiveUpdate method for each observer. Notice that we use the insert operator notation instead of observers.foreach. ) We use the placeholder'_'to shorten the following expression
(obs) = > obs.receiveUpdate (this)
For such an expression
_ .receiveUpdate (this)
This expression is actually the body of an "anonymous function", which is called a literal function in Scala. This is similar to Lambda expressions or similar structures in other languages. Concepts related to literal functions and closures are discussed in the "literal functions and closures" section of Chapter 8-Scala functional programming.
In Java, the foreach method will most likely accept an interface, and you may pass an instance of the class that implements the interface. (for example, the method used by a typical Comparable).
In Scala, the List[ A] .foreach method expects a parameter of type (A) = > Unit, which is a function that takes an argument of type A, which identifies the type of the element of the list (in this case, Observer) and returns Unit (like Java's void).
Be careful
In this example, we choose to use a var to represent the List of the immutable observer. We can also use val and a mutable type, such as ListBuffer. This choice would make more sense in a real application, but we want to avoid introducing new classes to distract us.
Once again, we learned a lot about Scala from a small example. Now, let's use our Subject Trait. Here is an ObservableButton that inherits Button and mixes Subject.
/ / code-examples/Traits/ui/observable-button.scala package ui import observer._ class ObservableButton (name: String) extends Button (name) with Subject {override def click () = {super.click () notifyObservers}}
We start by importing everything in the observer package, using the'_ 'wildcard character. In fact, we only define Subject Trait in this package.
The new class uses the with keyword to add Subject Trait to the class. ObserverButton overrides the click method. Using the super keyword (see the section "overriding abstract and concrete methods" in Chapter 6-Scala Advanced object-oriented programming), it first calls the method of the "parent class", Button.click, which then notifies the observer. Because the new click method rewrites the concrete implementation of Button, the override keyword must be added.
The with keyword is similar to the implement keyword used by Java's peer interface. You can have as many Traits as you want, each of which must have a with keyword.
A class can inherit a Trait, and a Trait can inherit a class. In fact, our Widget class can also be declared as a Trait.
Be careful
If you define a class that uses one or more Traits and it does not inherit any class, you must use the extends keyword for the first listed Trait.
If you don't use extends for the first Trait, for example, write like this.
/ / ERROR: class ObservableButton (name: String) with Button (name) with Subject {.}
You will get the following error.
... Error:'; 'expected but' with' found. Class ObservableButton (name: String) with Button (name) with Subject {...} ^
This mistake should actually say "with found,but extends expected." Found the with keyword, but expected an extends. )
To demonstrate this part of the code, let's start with a class that watches button clicks and records the number of clicks.
/ / code-examples/Traits/ui/button-count-observer.scala package ui import observer._ class ButtonCountObserver {var count = 0 def receiveUpdate (subject: Any) = count + = 1}
On the left, let's write a test to use all the classes. We will use the Specs library (discussed in the "Specs" section of Chapter 14-Scala tools, Libraries and IDE support) to write a behavior-driven ([BDD]) "specification" to test the combined Button and Subject types.
/ / code-examples/Traits/ui/button-observer-spec.scala package ui import org.specs._ import observer._ object ButtonObserverSpec extends Specification {"A ButtonObserver" should {"observe button clicks" in {val observableButton = new ObservableButton ("Okay") val buttonObserver = new ButtonCountObserver observableButton.addObserver (buttonObserver) for (I
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.