Scala trait explanation [trait inheritance form, object blending trait, trait adapter mode, template method mode, responsibility chain mode, construction mechanism of trait, trait inheritance class]

Posted by esscher on Wed, 05 Jan 2022 21:39:20 +0100

Idiosyncrasy

summary:

Sometimes, we will encounter some specific needs, that is, to strengthen the functions of some classes (or objects) without affecting the current inheritance system. For example, there are monkeys and elephants. They all have names, ages and eating functions, but some monkeys learn to ride unicycles after circus training, Then the function of riding a unicycle should not be defined in the parent (animal) or monkey, but in the trait. The trait in Scala should be decorated with the keyword trait.

Note: the characteristics in Scala are very similar to the interfaces in Java

characteristic:

  • Characteristics can improve the reusability of code
  • Characteristics can improve the scalability and maintainability of code.
  • The inheritance relationship between classes and traits only supports single inheritance between classes, but between classes and traits, both single inheritance and multiple inheritance can be supported.
  • Scala can have common fields, abstract fields, common methods and abstract methods.

be careful:

  • 1. If there is only abstract content in traits, such traits are called thin traits.
  • 2. If there are both abstract content and concrete content in the characteristics, such characteristics are called complex interface.

Syntax:

Defining traits:

trait Trait name{
	// General field 
	// Abstract field 
	
	// Common method 
	// Abstract method
}

class class extends Trait 1 with Trait 2 { 
// Override abstract field 
// Override abstract methods 
}

be careful:

  • In Scala, whether it is a trait or a class, the inheritance relationship uses the extends keyword.
  • If you want to inherit more than one trait, the trait names are separated by the with keyword.

Class inherits a single trait

Code example:

  1. Create a Logger and add a log(msg:String) method
  2. Create a ConsoleLogger class, inherit the Logger characteristics, implement the log method, and print messages
  3. Add the main method, create the ConsoleLogger object, and call the log method
object Class inherits a single trait {
  //1. Define a trait
  trait Logger {
    def log(msg:String) //Abstract method
  }

  //2. Define a class inheritance attribute
  class ConsoleLogger extends Logger {
    override def log(msg: String): Unit = println(msg)
  }

  def main(args: Array[String]): Unit = {
    //3. Call the method in the class
    val cl = new ConsoleLogger
    cl.log("trait: Class inherits a single trait")
  }

}
trait: Class inherits a single trait

Class inherits multiple traits

Code example:

  1. Create a MessageSender attribute and add the send(msg:String) method
  2. Create a MessageReceiver and add the receive() method
  3. Create a MessageWorker class, inherit these two characteristics, and override the above two methods
  4. Test in main and call send method and receive method respectively
object Class inherits multiple traits {
  //1. Define a characteristic: MessageSender, which means sending information.
  trait MessageSender {
    def send(msg:String)
  }
  //2. Define a feature: MessageReceiver, which means receiving information.
  trait MessageReceiver {
    def receive()
  }
  //3. Define a MessageWorker class that inherits two characteristics
  class MessageWorker extends MessageSender with MessageReceiver {
    override def send(msg: String): Unit = println("Send message:" + msg)

    override def receive(): Unit = println("The message has been received, I'm fine, thank you!!!")
  }

  //Main method as the main entry of the program
  def main(args: Array[String]): Unit = {
    //4. Call the method in the class
    val mw = new MessageWorker
    mw.send("Hello,How do you do!!!")
    mw.receive()
  }

}
Send message: Hello,How do you do!!!
The message has been received, I'm fine, thank you!!!

object inheritance trait

Code demonstration:

  1. Create a Logger and add a log(msg:String) method
  2. Create a Warning feature and add a warn(msg:String) method
  3. Create a singleton object ConsoleLogger, inherit the Logger and Warning attributes, and rewrite the abstract methods in the attributes
  4. Write the main method and call the log and warn methods of the singleton object ConsoleLogger
object Object Inherited traits trait {

  //1. Define a trait Logger and add the log(msg:String) method
  trait Logger {
    def log(msg:String)
  }

  //2. Define a Warning and add a warn(msg:String) method
  trait Warning {
    def warn(msg:String)
  }

  //3. Define the singleton object ConsoleLogger, inherit the above two characteristics, and override the two methods
  object ConsoleLogger extends Logger with Warning {
    override def log(msg: String): Unit = println("Console log information:" + msg)

    override def warn(msg: String): Unit = println("Console warning message:" + msg)
  }

  //main method as the entry of the program
  def main(args: Array[String]): Unit = {
    //4. Call two methods in the ConsoleLogger singleton object
    ConsoleLogger.log("I am an ordinary log message")
    ConsoleLogger.warn("I am a warning log message")
  }

}
Console log information: I am an ordinary log information
 Console warning message: I am a warning log message

Members in the presentation

Code example:

  1. Define a trait Hero, add the concrete field name, the abstract field arms, the concrete method eat(), and the abstract method toWar()
  2. Define a class general, inherit the Hero trait, and override all abstract members
  3. In the main method, create an object of the general class and call its members
object Members in the presentation {
  //1. Define a trait Hero
  trait Hero {
    var name = ""   //Specific field
    var arms:String   //Abstract field

    //Specific method
    def eat() = println("")

    //Abstract method
    def toWar():Unit
  }

  //2. Define a class general, inherit the Hero trait, and rewrite all abstract members
  class Generals extends Hero {
    //Override abstract fields in parent attributes
    override var arms: String = ""

    //Override abstract methods in parent attributes
    override def toWar(): Unit = println(s"${name}With ${arms},Fight the enemy!")
  }

  //main method as the entry of the program
  def main(args: Array[String]): Unit = {
    //3. Create an object of the general class
    val gy = new Generals

    //4. Test the contents of the general class
    //Assign values to member variables
    gy.name = "Guan Yu"
    gy.arms = "falchion"
    //Print member variable values
    println(gy.name, gy.arms)
    //Call member method
    gy.eat()
    gy.toWar()
  }

}
(Guan Yu,falchion)

Guan Yu takes Qinglong Yanyue knife and goes to battle to kill the enemy!

Object blending trait

Sometimes, we want to temporarily enhance or extend the functions of objects without changing class inheritance. At this time, we can consider using object blending technology. The so-called object blending means that in Scala, there is no inheritance relationship between classes and traits, but class objects can have members in traits through specific keywords.

Syntax:

val/var Object name = new class with Idiosyncrasy

Code example:

  1. Create a Logger attribute and add a log(msg:String) method
  2. Create a User class that has no relationship with the Logger attribute
  3. Test the log() method in the main method, which makes the object of User class have Logger characteristics through object blending technology
object Object blending trait {

  //1. Create a Logger attribute and add a log(msg:String) method
  trait Logger {
    def log(msg:String) = println(msg)
  }

  //2. Create a User class, which has no task relationship with the Logger attribute
  class User

  //main method as the entry of the program
  def main(args: Array[String]): Unit = {
    //3. Test the log() method in the main method to make the object of User class have Logger characteristics through object blending technology
    val cl = new User with Logger //Object blending
    cl.log("I am User Class, I can call Logger In trait log Method")
  }

}
I am User Class, I can call Logger In trait log Method

Using trait to implement adapter pattern

Design pattern

summary:
Design Pattern is the experience of code development summarized by predecessors and a series of routines to solve specific problems. It is not a syntax specification, but a set of solutions to improve code reusability, maintainability, readability, robustness and security.

Design patterns are classified as follows:

1. Create type
It refers to the object that needs to be created Common modes are: Singleton mode and factory method mode
2. Structural type
It refers to the relationship structure between classes and traits The common modes are adapter mode and decoration mode
3. Behavioral
It refers to what a class (or trait) can do Common modes include template method mode and responsibility chain mode

Adapter mode
When there are many abstract methods in a trait and we only need to use one or several of them, we have to rewrite all the abstract methods in the trait. This is very troublesome. In view of this situation, we can define an abstract class to inherit the trait and rewrite all the abstract methods in the trait. The method body is empty. At this time, we only need to define the class, inherit the abstract class and override the specified method. This abstract class is called adapter class. This design pattern (design idea) is called adapter pattern.

The structure is as follows:

trait Idiosyncrasy A{ 
//Abstract method 1 
//Abstract method 2 
//Abstract method 3 / / 
}
abstract class class B extends A{ //adapter class  
//Override abstract method 1, method body is empty 
//Override abstract method 2, method body is empty 
//Override abstract method 3, method body is empty / / 
}

class Custom class C extends class B { 
//You can override which method you need to use 
}

Code example:

  1. Define PlayLOL and add 6 abstract methods: top(), mid(), adc(), support(), jungle(), schoolchild()
    Explanation: top: on order, mid: medium order, adc: off road, support: auxiliary, jungle: playing wild, schoolchild: primary school students
  2. Define the abstract class Player, inherit the PlayLOL feature, override all abstract methods in the feature, and the method body is empty
  3. Define common class GreenHand, inherit Player, override support() and schoolchild() methods
  4. Define the main method, create the object of GreenHand class in it, and call its method to test
object adapter design pattern  {

  //1. Define PlayLOL and add 6 abstract methods: top(), mid(), adc(), support(), jungle(), schoolchild()
  trait PlayLOL {
    def top()   //top
    def mid()   //mid
    def adc()   //Down the road
    def support()   //auxiliary
    def jungle()    //Fight wild
    def schoolchild()   //pupil
  }

  //2. Define the abstract class Player, inherit the PlayLOL feature, rewrite all abstract methods in the feature, and the method body is empty
  //The role of the Player class is the adapter class
  class player extends PlayLOL {
    override def top(): Unit = {}
    override def mid(): Unit = {}
    override def adc(): Unit = {}
    override def support(): Unit = {}
    override def jungle(): Unit = {}
    override def schoolchild(): Unit = {}
  }

  //3. Define a common class GreenHand, inherit Player, and override abstract methods.
  class GreenHand extends PlayLOL{
    override def top(): Unit = println("Come on, Galen!")
    override def mid(): Unit = println("Zhongdan Yasuo!")
    override def adc(): Unit = println("adc Ice!")
    override def support(): Unit = println("Help Bren!")
    override def jungle(): Unit = println("Fight wild sword saint!")
    override def schoolchild(): Unit = println("I'm a pupil, You scold me, I'll hang up!")
  }

  //4. Define the main method, create the object of GreenHand class in it, and call its method for testing
  def main(args: Array[String]): Unit = {
    //Create an object of the GreenHand class
    val gh = new GreenHand
    //Call a method in the GreenHand class
    gh.support()
    gh.schoolchild()
  }

}
Help Bren!
I'm a pupil, You scold me, I'll hang up!

Using trait to implement template method pattern

In real life, we will encounter paper templates, resume templates, including some templates in PPT, etc. in the process of object-oriented programming, programmers often encounter this situation: when designing a system, they know the key steps required by the algorithm and determine the execution order of these steps, but some steps are different
The specific implementation is unknown, or the implementation of some steps is related to the specific environment.

For example, to handle business in a bank, you generally go through the following four processes: number taking, queuing, handling specific business, scoring bank staff, etc. among them, the business of number taking, queuing and scoring bank staff is the same for each customer and can be implemented in the parent class, but handling specific business depends on people
It may be deposit, withdrawal or transfer, which can be deferred to subclasses. This requires the use of template design patterns

summary:
In Scala, we can first define an algorithm skeleton in operation, and delay some steps of the algorithm to the subclass, so that the subclass can redefine some specific steps of the algorithm without changing the algorithm structure, which is template method design.

advantage:

  • 1. Stronger scalability.
    • The public part is encapsulated in the parent class, and the variable part is implemented by the child class.
  • 2. Comply with the opening and closing principle.
    • Some methods are implemented by subclasses, so subclasses can add corresponding functions by extension.

Disadvantages:

  • 1. The increase in the number of classes leads to a larger system and more abstract design.
    • Because you need to define a subclass for each different implementation.
  • 2. Improve the difficulty of code reading.
    • Abstract methods in the parent class are implemented by subclasses, and the execution results of subclasses will affect the results of the parent class, which is a reverse control structure.

The format is as follows:

class A { //The parent class encapsulates the public part 
	def Method name(parameter list) = { //The specific method is also called template method 
		//Step 1, known 
		//Step 2, unknown, call the abstract method 
		//Step 3, known 
		//Step n 
	}
//Abstract method 
}
class B extends A { 
	//Override abstract methods 
}

Note: the number of abstract methods should be determined according to specific requirements. There may not be only one, but also multiple.

Code example:

  1. Define a Template class Template and add code() and getRuntime() methods to get the execution time of some code
  2. Define the class ForDemo, inherit the Template, and then override the code() method to calculate and print "Hello,Scala!" 10000 times Execution time of
  3. Define the main method to test the specific execution time of the code
object Template method design pattern {

  //The getTemplate () method is used to get the execution time of some templates and a getTemplate () method
  abstract class Template {
    //Define the code() method to record all the code to be executed
    def code()

    //Define template methods to get the execution time of some code
    def getRuntime() = {
      //Gets the current time in milliseconds
      val start = System.currentTimeMillis()
      //Specific code to execute
      code()
      //Gets the current time in milliseconds
      val end = System.currentTimeMillis()
      //Returns the execution time of the specified code
      end - start

    }
  }

  //2. Define the class ForDemo to inherit the Template, and then override the getRuntime() method to calculate and print "Hello,Scala!" 10000 times Execution time of
  class ForDemo extends Template {
    override def code(): Unit = for(i <- 1 to 10000) println("Hello Scala!")
  }

  def main(args: Array[String]): Unit = {
    //3. Test and print 10000 times "Hello, Scala!" Execution time of
    println(new ForDemo().getRuntime())
  }

}

Using trait to implement responsibility chain pattern

summary:
The same method appears in multiple traits, and the method finally calls super The method name (). After the class inherits the multiple traits, it can call the same method in multiple traits in turn, forming a call chain.

The execution sequence is:

  • 1. Execute from right to left.
    • That is, first execute from the rightmost trait method, and then execute the methods in the corresponding trait to the left in turn.
  • 2. After the method of all child traits is executed, the method in the parent trait will be executed finally.
    • Note: in Scala, the case where a class inherits multiple traits is called superposition traits.

The syntax format is as follows:

trait A { //Paternity 
	def show() //The hypothetical method is called show 
}
trait B extends A { //You can define multiple sub characteristics according to your needs 
	override def show() = { 
		//Specific code logic 
		super.show() 
	} 
}
trait C extends A { 
	override def show() = { 
		//Specific code logic 
		super.show() 
	} 
}
class D extends B with C { //Specific classes are used to demonstrate: superposition characteristics 
	def Method name() = { //This can be the class's own method, not necessarily the show() method 
		//Specific code logic 
		super.show() //This constitutes the call chain 
	} 
}
/* 
The execution sequence is: 
	1. First execute your own method in class D 
	2. Then execute the show() method in C 
	3. Then execute the show() method in Figure B 
	4. Finally, execute the show() method in A 
*/

Code demonstration: (implement a call chain simulating the payment process through Scala code.)

Explanation:
If we want to develop a payment function, we often need to perform a series of verification to complete the payment. For example: verification of payment signature; Data validity verification; wait. If more verification rules need to be added due to the adjustment of third-party interface payment in the future, how can the previous verification code not be modified to realize the extension??

This requires: responsibility chain design pattern.

Illustration:

Steps:

  1. Define a Handler feature and add a specific handle(data:String) method to represent the processing data (specific payment logic) 2 Define a DataValidHandler trait and inherit the Handler trait
    Overwrite the handle() method, print the validation data, and then invoke the handle() method of the parent attribute.
  2. Define a signaturevalhandler trait and inherit the Handler trait
    Overwrite the handle() method, print "check the signature", and then invoke the handle() method of the parent attribute.
  3. Create a Payment class that inherits the DataValidHandler and signaturevalhandler attributes
    Define the pay(data:String) method, print the "user initiated payment request", and then call the parent attribute handle() method.
  4. Add the main method, create the Payment object instance, and then call the pay() method.
object Responsibility chain model design {
  //1. Define a parent Handler to represent data processing (specific payment logic)
  trait Handler {
    def handle(data:String) = {
      println("Specific data processing code (e.g. transfer logic)")
      println(data)
    }
  }
  //2. Define a subdatavalidhandler to represent the verification data
  trait DataValidHandler extends Handler{
    override def handle(data: String) = {
      println("Check data...")
      super.handle(data)
    }
  }
  //3. Define a sub signature validhandler, which represents the verification signature
  trait SignatureValidHandler extends Handler {
    override def handle(data: String) = {
      println("Verification signature...")
      super.handle(data)
    }
  }
  //4. Define a class Payment, which represents the Payment request initiated by the user
  class Payment extends DataValidHandler with SignatureValidHandler {
    def pay(data:String) = {
      println("User initiated payment request...")
      super.handle(data)
    }
  }

  def main(args: Array[String]): Unit = {
    //5. Create the object of Payment class and simulate: call chain
    val pm = new Payment
    pm.pay("shuyv to xin Transfer 10000¥")
  }
  // The program operation output is as follows:
  // User initiated payment request
  // Verify signature
  // Check data
  // Specific data processing code (e.g. transfer logic)
  // Su Mingyu transferred 10000 yuan to Su Daqiang

}
User initiated payment request...
Verification signature...
Check data...
Specific data processing code (e.g. transfer logic)
shuyv to xin Transfer 10000¥

Construction mechanism of trait

summary:
If a class inherits a parent class and inherits multiple parent traits, how is the class (subclass), its parent class, and its parent traits constructed?

To solve this problem, we need to use the construction mechanism of trait.

Construction mechanism rules:

  • Each trait is a parameterless constructor.
    • In other words, trait also has construction code, but unlike classes, traits cannot have constructor parameters.
  • When one class inherits another class and multiple trait s are encountered, when creating an instance of this class, its constructor execution order is as follows:
    • 1. Constructor according to the parent class.
    • 2. Execute the constructor of trait from left to right at one time.
    • 3. If a trait has a parent trait, execute the constructor of the parent trait first.
    • 4. If multiple traits have the same parent trait, the constructor of the parent trait is initialized only once.
    • 5. Execute the subclass constructor.

Code example:

Define a parent class and multiple attributes, and then inherit them with a class
Create subclass objects and test the construction order of trait. The steps are as follows:

  1. Create a Logger and print "execute Logger constructor!" in the constructor
  2. Create a MyLogger trait, inherit from the Logger trait, and print "execute MyLogger constructor!" in the constructor
  3. Create a TimeLogger trait, inherit from the Logger trait, and print "execute TimeLogger constructor!" in the constructor
  4. Create the Person class and print "execute Person constructor!" in the constructor
  5. Create a Student class, inherit the Person class, mylogger and timelogge characteristics, and print "execute Student constructor" in the constructor
  6. Add the main method, create the object of Student class, and observe the output.
object trait Construction mechanism of {
  //1. Create a Logger parent
  trait Logger {
    println("implement Logger constructor ")
  }
  //2. Create a MyLogger child trait and inherit the Logger trait
    trait MyLogger extends Logger {
    println("implement MyLogger constructor ")
  }
  //3. Create a TimeLogger child trait and inherit the Logger trait
  trait TimeLogger extends Logger {
    println("implement TimeLogger constructor ")
  }
  //4. Create the parent class Person
  class Person {
    println("implement Person constructor ")
  }
  //5. Create a subclass Student, inherit the Person class and the characteristics of TimeLogger and MyLogger
  class Student extends Person with TimeLogger with MyLogger {
    println("implement Student constructor ")
  }
  //main method, the entry of the program
  def main(args: Array[String]): Unit = {
    //6. Create the object of Student class and observe the output.
    new Student
  }
}
implement Person constructor 
implement Logger constructor 
implement TimeLogger constructor 
implement MyLogger constructor 
implement Student constructor 

trait inherits class

summary:
In Scala, trait can also inherit class. The trait will inherit all the members in the class.

Syntax format:

class class A { //Class A 
	//Member variable 
	//Member method 
}

trait B extends A { //Trait B 
}

Code example:

  1. Define the Message class Add the printMsg() method and print "learn Scala well and don't be afraid to go anywhere!"
  2. Create a Logger attribute and inherit the Message class
  3. Define the ConsoleLogger class and inherit the Logger characteristics
  4. In the main method, create an object of the ConsoleLogger class and call the printMsg() method
object Trait inheritance class {
  //1. Define Message class Add printMsg() method to print "test data..."
  class Message {
    def printMsg() = println("learn from good examples Scala,Well done Spark!")
  }
  //2. Create the Logger attribute and inherit the Message class
  trait Logger extends Message
  //3. Define the ConsoleLogger class and inherit the Logger characteristics
  class ConsoleLogger extends Logger

  def main(args: Array[String]): Unit = {
    //4. Create the object of the ConsoleLogger class and call the printMsg() method
    val cl = new ConsoleLogger
    cl.printMsg()
  }

}
learn from good examples Scala,Well done Spark!

Topics: Scala