preface

I’m sure you all have experience with object orientation, so what does object orientation feel like?

Well, I guess it starts with a racing heart, a pounding heart, then nothing, and then she tortures you into doubting your life. Dart: I’d like to introduce you to someone today. Her name is Dart.


1. Object-oriented conditions

1.1: Three features

First of all, the house, the car, the ticket should have, otherwise return a face what object? Secondly, the idea of object-oriented should be in place, and three major preparations: encapsulation, inheritance, polymorphism

1.1.1: Encapsulation idea

And here’s something you’ve probably seen before, which is an electronic component, and it’s a good example of how packaging works. Each electronic component has exposed pins. These feet are divided into input and output. When we use an electronic component, we care only about its input and output, that is, its truth table, not its internal composition.

So an electronic component to a hardware assembler is a black block with pins, a logical unit capable of performing a specific function. Analogies to tripartite libraries. After introduction, there is no need to know the implementation logic of the library. As long as you make an API call (input) according to the exposed API(truth table), a specific function (output) will be completed.


The same is true of classes, where the ultimate goal is to reuse logic as a stand-alone unit of logic that can work with other classes. But here’s the difference: we’re not just users, we’re designers. So the important thing to consider is not only how this class works with other classes, but more importantly its internal composition.

You know, using an electronic component is very different from designing and building an electronic component, and the programmer is obviously the latter.


1.1.2: The idea of inheritance

A person is not born with nothing. He or she enjoys the assets, connections and status of his or her parents. These are all resources at his disposal. There is no need to struggle to get where you are now so that you can move up in the future. Of course, the same is true of classes, and subclasses can inherit from their parents to enjoy the ‘gift’.

This brings us to a concept called abstraction. Abstract is not taken casually, not to solve the problem as the premise of the abstract are playing rogue, through abstraction to extract the common characteristics of the object, the formation of the base class. This is the equivalent of parents struggling to create a suitable living environment for their children.

Another point is that children don’t necessarily inherit everything from their fathers, and there are certain things fathers don’t want to give to their children, which involves access restrictions on inheritance.


1.1.3: Class polymorphism

A person can have multiple roles in society. For example, Jet is a student at school, a programmer at work, a boyfriend at weekends, and a tourist on trips. These are the different states of an object, called polymorphisms for short.

What are the advantages of polymorphism? For example, someone shouted, come to a student to move the table, jet can go as a student; Weekend sister about, Jet can boyfriend identity in the past; Someone shouted, let a programmer fix the bug, jet can run there. The advantage of this is that the method that uses the object only needs to be for the interface, and does not need to pass in the Me object. If pickDesk, Date, and fixBug are all passed into Me objects, it works, but it’s not easy to expand and maintain.

class Me implements Pickable,Kissable,Codeable{

}

pickDesk(Pickable student){
  student.pick(this);
}

date(Kissable boyfriend){
  if(boyfriend.getId == this.id){
    boyfriend.kiss(this);
  }
}

fixBug(Codeable coder){
  coder.debug(this);
}
Copy the code

Okay, rory, let’s get to the point. Dart object orientation is usually a Person with a Student in it.

Although there is nothing bad, but the feeling is very bland, since we are object-oriented experience, directly to the source to find the object face bai, so it is thrilling, exciting.


2. Start with the Size family

I have been thinking through the source code of that class to start to say it is better, the best not too long, not too difficult, Size is more perfect.

2.1. Class definition

The class keyword defines a class, OK, extend extends the keyword, also OK.

---->[sky_engine/lib/ui/geometry.dart:347]-------
class Size extends OffsetBase {
Copy the code

2.2. Definition of abstract classes

Size inherits from OffsetBase, so let’s look at OffsetBase. First of all, it is an abstract class and uses the keyword abstract.

There is a constructor that takes in two parameters, the horizontal and vertical values. Note that constructors are written differently from Java. It encapsulates two private attributes: _dx and _dy,

---->[sky_engine/lib/ui/geometry.dart:9]------- abstract class OffsetBase { const OffsetBase(this._dx, this._dy); // Constructor final double _dx; final double _dy; }Copy the code

Method: 2.3

I don’t know how you feel when you see the code below, but I feel uncomfortable, maybe not familiar with it.

/ / if cannot measure the bool get isInfinite = > _dx > = double. Infinity | | _dy > = double. Infinity; Bool get isFinite => _dx.isfinite && _dy.isfinite; It's a little scary, but it's just the beginning. You can analyze it slowly. If you're dazzled by the above sentence, I've rewritten it. Which means if there is a more than double _dx and _dy range will return true bool isInfinite () {return _dx > = double. Infinity | | _dy > = double. Infinity; Var size= size (30,40); print(size.isInfinite); //falseCopy the code

2.4: Operator overloading

I looked at this code for three seconds. There were too many arrows, but I didn’t realize it. On second thought, this is not an operator overload. The following overloads the operators <, <=, >, >=, ==, respectively.

bool operator <(OffsetBase other) => _dx < other._dx && _dy < other._dy; bool operator <=(OffsetBase other) => _dx <= other._dx && _dy <= other._dy; bool operator >(OffsetBase other) => _dx > other._dx && _dy > other._dy; bool operator >=(OffsetBase other) => _dx >= other._dx && _dy >= other._dy; @override bool operator ==(dynamic other) { if (other is! OffsetBase)// If the incoming object is not OffsetBase return false; // Roll final OffsetBase typedOther = other; Return _dy == typedother._d&&; // return _dx == typedother._dy; } var size= size (30,40); Var size2 = Size (10, 20); print(size>size2); //trueCopy the code

2.5: Hash and toString

Compared to Java, there is nothing to say. Here to insert, when looking at the source code, it is best to pay attention to the writing style of the source code, after all, it is necessary to keep up with the big guy, such as naming style, annotation style and so on.

@override int get hashCode => hashValues(_dx, _dy); @override String toString() => '$runtimeType(${_dx? .toStringAsFixed(1)}, ${_dy? .toStringAsFixed(1)})';Copy the code

OK, so an OffsetBase object is finished, not tight, not prickling. Let’s move on to Size


2.6: Construct the Size object method

Notice, circle it here. It’s a test. Use the parent constructor to construct the class in the following syntax: class name (parameter,…) : super (parameters,…).

Class Size extends OffsetBase {const Size(double width, double height) : super(width, height);Copy the code

When you think about the confusing sentence in the initial project, it suddenly becomes clear.

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
Copy the code

And then there’s a bunch of ways to construct Size objects, so basically, anyway, can you give me two values for width and height

Size.copy(Size source) : super(source.width, source.height); const Size.square(double dimension) : super(dimension, dimension); const Size.fromWidth(double width) : super(width, double.infinity); const Size.fromHeight(double height) : super(double.infinity, height); Const size. fromRadius(double radius) : super(radius * 2.0, radius * 2.0); Static const Size zero = const Size(0.0, 0.0); static const Size infinite = const Size(double.infinity, double.infinity);Copy the code

Attribute encapsulation in 2.7:Size

After having daddy, Size class can use Daddy’s “wealth”, here Size has changed the name of daddy’s things for the sake of image.

One of the values of classes is that they encapsulate attributes and manipulate relationships between attributes to perform specific functions, such as aspectRatio to obtain aspect ratios. For any Size object, at any time, in any space, you can call the aspectRatio method to obtain the aspectRatio, which is not procedural-oriented.

double get width => _dx; double get height => _dy; double get aspectRatio { if (height ! = 0.0) return width/height; If (width > 0.0) return double. Infinity; If (width < 0.0) return double. NegativeInfinity; The return of 0.0; }Copy the code

2.8: Advantages of inheritance

The Size class also has operator overloading. These are the four operations of Size. However, Size can still use the overloaded operator in OffsetBase.

OffsetBase operator -(OffsetBase other) {
  if (other is Size)
    return new Offset(width - other.width, height - other.height);
  if (other is Offset)
    return new Size(width - other.dx, height - other.dy);
  throw new ArgumentError(other);
}
Size operator +(Offset other) => new Size(width + other.dx, height + other.dy);
Size operator *(double operand) => new Size(width * operand, height * operand);
Size operator /(double operand) => new Size(width / operand, height / operand);
Size operator ~/(double operand) => new Size((width ~/ operand).toDouble(), (height ~/ operand).toDouble());
Size operator %(double operand) => new Size(width % operand, height % operand);
Copy the code
2.9. The Offset class

Inheritance will give children congenital advantages, so two children are more use. A parent class is defined to make it easier to extend, and OffsetBase is no exception.

The Size object describes a box-like object, so Offset describes displacement. The two have a common feature, that is, there are two numeric class attributes. That’s why OffsetBase is abstracted out.

Class Offset extends OffsetBase {// constructor const Offset(double dx, double dy) : super(dx, dy); factory Offset.fromDirection(double direction, [double distance = 1.0]) {return new Offset(distance * math.cos(direction), distance * math.sin(direction)); } static const Offset zero = const Offset(0.0, 0.0); static const Offset infinite = const Offset(double.infinity, double.infinity); double get dx => _dx; // double get dy => _dy; Double get distance => math.sqrt(dx * dx + dy * dy); double get distance => math.sqrt(dx * dx + dy * dy); Double get distanceSquared => dx * dx + dy * dy; Offset scale(double scaleX, double scaleY) => new Offset(dx * scaleX, dy * scaleY); // Offset translate(double translateX, double translateY) => new Offset(dx + translateX, dy + translateY); Offset operator -() => new Offset(-dx, -dy); Offset operator -(Offset other) => new Offset(dx - other.dx, dy - other.dy); Offset operator +(Offset other) => new Offset(dx + other.dx, dy + other.dy); Offset operator *(double operand) => new Offset(dx * operand, dy * operand); Offset operator /(double operand) => new Offset(dx / operand, dy / operand); Offset operator %(double operand) => new Offset(dx % operand, dy % operand); Rect operator &(Size other) => new Rect.fromLTWH(dx, dy, other.width, other.height); / / a little... @override int get hashCode => hashValues(dx, dy); @override String toString() => 'Offset(${dx? .toStringAsFixed(1)}, ${dy? .toStringAsFixed(1)})';Copy the code
2.10: summary

These are three classes that are easy to read and draw if you’re new to Dart.

You should now have an understanding of class creation, property, method writing, and class inheritance in Dart.


3.Dart interfaces and enumerations

Unlike Java, the interface definition in Dart is still the Abstract keyword. Interfaces and abstract classes are essentially the same. The Comparable and Pattern interfaces are examples.

3.1: Interface definition

Comparable defines an abstract method, compareTo, to compare with another object, which is the function of this interface to compare.

The function of the Pattern interface is to get an iterable matching result set by matching a string.

---->[sky_engine/lib/core/comparable.dart:72]----------
abstract class Comparable<T> {

  int compareTo(T other);
  
  static int compare(Comparable a, Comparable b) => a.compareTo(b);
}

---->[sky_engine/lib/core/pattern.dart:72]----------
abstract class Pattern {
  Iterable<Match> allMatches(String string, [int start = 0]);
  Match matchAsPrefix(String string, [int start = 0]);
}
Copy the code

3.2:Dart interface

The class that implements an interface owns the functionality of that interface. You can see that NUM implements the Comparable interface, demonstrating its ability to compare.

The implementation of the interface, like Java, is the keyword implements.

abstract class num implements Comparable<num> {

Copy the code

3.3: Implement multiple interfaces

The interfaces in Dart also support multiple implementations, separated by commas. For example, String implements both Comparable and Pattern,

Note String supports both functions.

abstract class String implements Comparable<String>, Pattern {
Copy the code

3.4: Enumeration type

Enumerations are commonly used to represent a group of constants of the same type, represented by the keyword enum.

Enumeration objects can branch with switches. In addition, enumeration elements in Dart have indexes that start at 0 and are counted sequentially, accessed with the index attribute. When I think of enumerations, the first thing THAT comes to mind is the Paint header, which I’ll use to illustrate:

-- -- -- - > [sky_engine/lib/UI/painting. The dart: 833] -- -- -- -- -- -- -- -- -- -- enum StrokeCap {butt, / / the headless (default) round, / / round square, square} / / use:  var paint= Paint(); paint.strokeCap=StrokeCap.round; / / circular print (StrokeCap. Round. The index); / / 1Copy the code

It seems that several classes in the source code, object-oriented also basically can have an understanding. So let’s sum it up by defining a two vector.


4. Custom vector class Vector2

4.1: Define and use Vector2 classes

Here we define a Vector2 class, which contains two values, horizontal and vertical, and a primitive constructor

class Vector2{ num x; num y; Vector2(num x, num y) { this.x = x; this.y = y; }} main(){var v1=Vector2(3,4); Print (' (${v1. X}, ${v1. Y}) "); / / (3, 4)}Copy the code

4.2: Short for constructor

Using shorthand, you can make the constructor concise and achieve its equivalent.

class Vector2{
  num x;
  num y;
  Vector2(this.x,this.y);
}
Copy the code

4.3: Named constructors

XXX. FormXXX is often found in source code to construct objects

class Vector2{ num x; num y; Vector2.fromMap(Map<String,num> point){ this.x = point['x']; this.y = point['y']; }} the main () {/ / using var v2 = Vector2 fromMap ({' x '5,' y ': 6}); Print (' (${v2. X}, ${v2. Y}) "); / / (5, 6)}Copy the code

4.4: Define methods
double get distance => sqrt(x * x + y * y); Double get rad => atan2(y, x); double get rad => atan2(y, x); // double get Angle => 180 / PI * atan2(y, x); // The Angle with the positive direction of xCopy the code

4.5: Define an operation interface
abstract class Operable{ void reflex(); // reverse void reflexX(); Void reflexY(); //Y reverse void scale(num xRate,num yRate); Void translate(num dx,num dy); Rotate (num deg,[isAnticlockwise=true]); rotate(num deg,[isAnticlockwise=true]); / / rotating}Copy the code

4.6: Inheritance interface, implementation method
class Vector2 implements Operable{ num x; num y; Vector2(this.x,this.y); Vector2.fromMap(Map<String,num> point){ this.x = point['x']; this.y = point['y']; } @override void reflex() { this.x=-x; this.y=-y; } @override void reflexX() { this.x=-x; } @override void reflexY() { this.y=-y; } @override void rotate(num deg, [isAnticlockwise = true]) { var curRad = rad + deg*pi/180; this.x=distance*cos(curRad); this.y=distance*sin(curRad); } @override void scale(num xRate, num yRate) { this.x *=xRate; this.y *=yRate; } @override void translate(num dx, num dy) { this.x +=dx; this.y +=dy; } @override String toString() {return '(${this.x},${this.y}) '; }}Copy the code

4.7: Operator overloading

Learn now, sell now, and reload the operator

// Operator +(Vector2 other) => Vector2(x + other.x, y + other.y); Vector2 operator -(Vector2 other) => Vector2(x - other.x, y - other.y); num operator *(Vector2 other) => x * other.x + y * other.y;Copy the code

This class is left as it is and needs to be improved in the future.


4.8: test
main() { var v1 = Vector2(3, 4); print(v1); / / (3, 4) print (v1. Short); / / 5.0 print (v1. Angle); / / 53.13010235415598 v1. Rotate (37); print(v1); / / (0.011353562466313798, 4.0000058005648444) var v2 = Vector2. FromMap ({' x '5,' y ': 6}); print(v2); / / (5, 6) v2. Reflex (); print(v2); Var Vector2 = Vector2(2, 2); var v4 = Vector2(3, 2); print(v4 - v3); //(1,0) print(v4 + v3); //(5,4) print(v4 * v3); / / 10}Copy the code

In addition, I have a Flutter wechat communication group. You are welcome to join and discuss Flutter issues together. My wechat account is ZDL1994328. If you want to get a quick taste of Flutter, Flutter 7 is a must-have.

The next article will sweep the syntax up and introduce the other syntax in Dart.

One last word: I wish you can really program for your object soon 👻