Skip to main content

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Article directory

      • OOP Versus Functional Decomposition
  • Adding Operations or Variants
  • Binary Methods with Functional Decomposition
  • Double Dispatch
  • Optional: Multimethods
  • Multiple Inheritance
  • Mixins
  • Interfaces
  • Optional: Abstract Methods

OOP Versus Functional Decomposition

A breakdown of object-oriented and functional (or procedural) programming

Programming Languages ��​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Example: Different variables and operations form a two-dimensional matrix.

Object-oriented and functional ways of thinking are different.

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Functional programming approach: fill each column through a function

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Object-oriented programming : populate each row by class

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

For Ruby:

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

For Java:

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

So, in fact, functional programming FP and object-oriented programming OOP can do the same thing, but they use two almost opposite ways. Which method to use depends on what you are doing and personal preference.

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Adding Operations or Variants

This section involves maintenance iterations of existing programs. For example, add operations or variables from the FP and OOP perspectives respectively.

For FP:

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

It is very easy to add methods to the code. You only need to add a function. However, adding variable objects is relatively troublesome. Not only do you need to add new variables to the originally defined structure, but you also need to modify all previously defined methods to adapt to the new method. Variables. It's easy to understand.

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

For OOP:

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

On the contrary, adding a variable only requires adding a new class (object), but adding new methods requires modifying all existing classes (although inheritance or some design patterns can be used to simplify this process). In short, it is not difficult to understand.

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Of course, we always hope to simplify this process so that when we write programs in one way (FP or OOP) we can also use the advantages of the other way (FP is good at new operations/methods, OOP is good at new variables/classes)

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Finally, there are some programming philosophies about program scalability.

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Binary Methods with Functional Decomposition

If there is an operation that needs to be defined based on many parameters (rather than a single variable call), such as a binary operation

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Example: The matrix here is different. It is not the relationship between Variants and Operation, but the relationship between two variables.

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

ML example: Because it needs to deal with the relationship between variants, Add does not need to throw an exception.

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Our eval procedure needs to define all cases (function decomposition) like in the green matrix

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Double Dispatch

Similar to the previous section, this section will use OOP in Ruby to implement similar add_values ​​operations, using a method called double dispatch.

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Similarly, first define the data structure, as follows, Rational is similar.

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

We always need to identify the classes of other objects in a certain class, so we need to call the method of the v object, but examples like the above are not object-oriented (explicitly determine the type of v). We do need to "tell" v, what type self is. This can be done using dynamic dispatch. This technique is called double-dispatch (that is, dispatching twice).

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Here is the direct reference code:

Each class still needs to define three methods (addInt, addRational, addString). The core idea is to call the corresponding method of v in the add_values ​​of each class. Since the class itself is determined, the corresponding method used by v can also be determined. For example v.addInt self, as for which subclass v is and which subclass method is called, it is determined by dynamic dispatch. This is the first dispatch .

Then v.addIntthe parameter v in the method is the previous self, which is certain (from the programmer's perspective), but the program v.istill needs to dynamically dispatch the method to determine which subclass when calling the method. This is the second dispatch .

    # Section 8: Binary Methods with OOP: Double Dispatch

# Note: If Exp and Value are empty classes, we do not need them in a
# dynamically typed language, but they help show the structure and they
# can be useful places for code that applies to multiple subclasses.

class Exp
# could put default implementations or helper methods here
end

class Value < Exp
# this is overkill here, but is useful if you have multiple kinds of
# /values/ in your language that can share methods that do not make sense
# for non-value expressions
end

class Int < Value
attr_reader :i
def initialize i
@i = i
end
def eval # no argument because no environment
self
end
def toString
@i.to_s
end
def hasZero
i==0
end
def noNegConstants
if i < 0
Negate.new(Int.new(-i))
else
self
end
end
# double-dispatch for adding values
def add_values v # first dispatch
v.addInt self
end
def addInt v # second dispatch: other is Int
Int.new(v.i + i)
end
def addString v # second dispatch: other is MyString (notice order flipped)
MyString.new(v.s + i.to_s)
end
def addRational v # second dispatch: other is MyRational
MyRational.new(v.i+v.j*i,v.j)
end
end

# new value classes -- avoiding name-conflict with built-in String, Rational
class MyString < Value
attr_reader :s
def initialize s
@s = s
end
def eval
self
end
def toString
s
end
def hasZero
false
end
def noNegConstants
self
end

# double-dispatch for adding values
def add_values v # first dispatch
v.addString self
end
def addInt v # second dispatch: other is Int (notice order is flipped)
MyString.new(v.i.to_s + s)
end
def addString v # second dispatch: other is MyString (notice order flipped)
MyString.new(v.s + s)
end
def addRational v # second dispatch: other is MyRational (notice order flipped)
MyString.new(v.i.to_s + "/" + v.j.to_s + s)
end
end

class MyRational < Value
attr_reader :i, :j
def initialize(i,j)
@i = i
@j = j
end
def eval
self
end
def toString
i.to_s + "/" + j.to_s
end
def hasZero
i==0
end
def noNegConstants
if i < 0 && j < 0
MyRational.new(-i,-j)
elsif j < 0
Negate.new(MyRational.new(i,-j))
elsif i < 0
Negate.new(MyRational.new(-i,j))
else
self
end
end

# double-dispatch for adding values
def add_values v # first dispatch
v.addRational self
end
def addInt v # second dispatch
v.addRational self # reuse computation of commutative operation
end
def addString v # second dispatch: other is MyString (notice order flipped)
MyString.new(v.s + i.to_s + "/" + j.to_s)
end
def addRational v # second dispatch: other is MyRational (notice order flipped)
a,b,c,d = i,j,v.i,v.j
MyRational.new(a*d+b*c,b*d)
end
end

class Negate < Exp
attr_reader :e
def initialize e
@e = e
end
def eval
Int.new(-e.eval.i) # error if e.eval has no i method
end
def toString
"-(" + e.toString + ")"
end
def hasZero
e.hasZero
end
def noNegConstants
Negate.new(e.noNegConstants)
end
end

class Add < Exp
attr_reader :e1, :e2
def initialize(e1,e2)
@e1 = e1
@e2 = e2
end
def eval
e1.eval.add_values e2.eval
end
def toString
"(" + e1.toString + " + " + e2.toString + ")"
end
def hasZero
e1.hasZero || e2.hasZero
end
def noNegConstants
Add.new(e1.noNegConstants,e2.noNegConstants)
end
end

class Mult < Exp
attr_reader :e1, :e2
def initialize(e1,e2)
@e1 = e1
@e2 = e2
end
def eval
Int.new(e1.eval.i * e2.eval.i) # error if e1.eval or e2.eval has no i method
end
def toString
"(" + e1.toString + " * " + e2.toString + ")"
end
def hasZero
e1.hasZero || e2.hasZero
end
def noNegConstants
Mult.new(e1.noNegConstants,e2.noNegConstants)
end
end


![Programming Languages ​​PartC Week2 study notes - OOP \(object-oriented\) vs FD \(functional\)](6b44e99974d17195ee2722671aabdc1f.png)

For the operation of Add, its eval method needs to first call the eval of its own e1 and e2 (it may be the eval of a data structure such as Int or the eval of the operation Add), dynamically dispatched, and let e1 and e2 decide to call the subclass method. ).

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

For statically typed languages ​​such as Java, double dispatch is also applicable, so double-dispatch is an important method to implement OOP binary operations.

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Let’s take a look at the Java code:

    // Section 8: Binary Methods with OOP: Double Dispatch

abstract class Exp {
abstract Value eval(); // no argument because no environment
abstract String toStrng(); // renaming b/c toString in Object is public
abstract boolean hasZero();
abstract Exp noNegConstants();
}

abstract class Value extends Exp {
abstract Value add_values(Value other); // first dispatch
abstract Value addInt(Int other); // second dispatch
abstract Value addString(MyString other); // second dispatch
abstract Value addRational(Rational other); // second dispatch
}

class Int extends Value {
public int i;
Int(int i) {
this.i = i;
}
Value eval() {
return this;
}
String toStrng() {
return "" + i;
}
boolean hasZero() {
return i==0;
}
Exp noNegConstants() {
if(i < 0)
return new Negate(new Int(-i));
else
return this;
}
Value add_values(Value other) {
return other.addInt(this);
}
Value addInt(Int other) {
return new Int(other.i + i);
}
Value addString(MyString other) {
return new MyString(other.s + i);
}
Value addRational(Rational other) {
return new Rational(other.i+other.j*i,other.j);
}
}

class MyString extends Value {
public String s;
MyString(String s) {
this.s = s;
}
Value eval() {
return this;
}
String toStrng() {
return s;
}
boolean hasZero() {
return false;
}

Exp noNegConstants() {
return this;
}

Value add_values(Value other) {
return other.addString(this);
}
Value addInt(Int other) {
return new MyString("" + other.i + s);
}
Value addString(MyString other) {
return new MyString(other.s + s);
}
Value addRational(Rational other) {
return new MyString("" + other.i + "/" + other.j + s);
}
}

class Rational extends Value {
int i;
int j;
Rational(int i, int j) {
this.i = i;
this.j = j;
}
Value eval() {
return this;
}
String toStrng() {
return "" + i + "/" + j;
}
boolean hasZero() {
return i==0;
}
Exp noNegConstants() {
if(i < 0 && j < 0)
return new Rational(-i,-j);
else if(j < 0)
return new Negate(new Rational(i,-j));
else if(i < 0)
return new Negate(new Rational(-i,j));
else
return this;
}
Value add_values(Value other) {
return other.addRational(this);
}
Value addInt(Int other) {
return other.addRational(this); // reuse computation of commutative operation

}
Value addString(MyString other) {
return new MyString(other.s + i + "/" + j);
}
Value addRational(Rational other) {
int a = i;
int b = j;
int c = other.i;
int d = other.j;
return new Rational(a*d+b*c,b*d);
}
}

class Negate extends Exp {
public Exp e;
Negate(Exp e) {
this.e = e;
}
Value eval() {
// we downcast from Exp to Int, which will raise a run-time error
// if the subexpression does not evaluate to an Int
return new Int(- ((Int)(e.eval())).i);
}
String toStrng() {
return "-(" + e.toStrng() + ")";
}
boolean hasZero() {
return e.hasZero();
}
Exp noNegConstants() {
return new Negate(e.noNegConstants());
}
}

class Add extends Exp {
Exp e1;
Exp e2;
Add(Exp e1, Exp e2) {
this.e1 = e1;
this.e2 = e2;
}
Value eval() {
return e1.eval().add_values(e2.eval());
}
String toStrng() {
return "(" + e1.toStrng() + " + " + e2.toStrng() + ")";
}
boolean hasZero() {
return e1.hasZero() || e2.hasZero();
}
Exp noNegConstants() {
return new Add(e1.noNegConstants(), e2.noNegConstants());
}
}

class Mult extends Exp {
Exp e1;
Exp e2;
Mult(Exp e1, Exp e2) {
this.e1 = e1;
this.e2 = e2;
}
Value eval() {
// we downcast from Exp to Int, which will raise a run-time error
// if either subexpression does not evaluate to an Int
return new Int(((Int)(e1.eval())).i * ((Int)(e2.eval())).i);
}
String toStrng() {
return "(" + e1.toStrng() + " * " + e2.toStrng() + ")";
}
boolean hasZero() {
return e1.hasZero() || e2.hasZero();
}

Exp noNegConstants() {
return new Mult(e1.noNegConstants(), e2.noNegConstants());
}
}


![Programming Languages ​​PartC Week2 study notes - OOP \(object-oriented\) vs FD \(functional\)](6b44e99974d17195ee2722671aabdc1f.png)

Optional: Multimethods

Using multimethod can avoid using double-dispatch to implement binary operations (essentially method overloading)

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Using multiple overloaded methods also has disadvantages, which can easily cause confusion in method calls.

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Although overloading methods by taking parameters from different subclasses is common in other languages, it is difficult to do in Ruby for two main reasons:

(1) First of all, ruby ​​dynamically typed language does not add type restrictions to method parameters.

(2) Secondly, ruby ​​does not allow functions with the same name other than overwriting (the same name means overriding, not overloading)

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

But in other static object-oriented languages, although a variety of method overloading is provided, they are only static overloading. Static typing needs to be specified at writing time (although it is still dynamically dispatched at runtime). But this has little to do with the so-called binary operations in our course examples, because the two operating objects in the course may be Int, Rational or String, but obviously in languages ​​such as Java, the type of this operating object is determined .

Dynamic typing was added to C# 4.0, so multimethod can also be implemented

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Multiple Inheritance

Multiple inheritance is also a common topic in OOP.

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Advantages and Disadvantages of Multiple Inheritance:

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

There may be ambiguities between inheritance structures:

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Mixins

Mixins refer to a collection of methods, which differ from classes in that they have no instances.

Ruby’s modules are mixins

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

example:

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Mixin changes the method search rules, first search in the class, then search in the mixin, then search in the super class, then search in the mixin of the super class, and so on.

For object variables, mixin methods can cause problems:

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Two of the most useful mixins in Ruby are Comparable and Enumerable.

Among them, comparison operators such as > and < are defined above < = > (that is, operations such as <, >, = and so on call < = > comparison)

Other iterators are defined on each

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

example:

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Interfaces

Compare the difference between multiple inheritance and mixins, and interfaces

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

If you have studied Java, you should be familiar with interfaces.

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Interfaces can be used together with mixins to ensure that certain methods must be implemented by the class (for example, Comparable needs to implement <=>, and Enumerable needs to implement each)

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Optional: Abstract Methods

This section is to introduce OOP in more detail, so it introduces abstract methods, such as Java's abstract methods and C++'s pure virtual functions.

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Generally, there will be superclasses that define certain methods that subclasses must override. In Ruby, we can (such as the example below), we can not define m2, but use it directly in m1. At this time, m2 is the subclass. The content must be defined, and we cannot create an instance of A alone with A.new, otherwise it will be method-missing.

It is worth paying special attention that this is not allowed in static languages. The parent class cannot call methods that are only declared and defined in the subclass; in ruby, you can directly call methods in the parent class that are only declared and defined in the subclass. Generally speaking, it is looser (only runtime can determine whether the method is defined by the parent class or subclass).

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

In a statically typed language, type checker will ensure that m2 must be declared and defined in the parent class, so we need some redundant statements (such as the raise statement part of the example below), which will be very redundant.

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Therefore, with abstract methods, they are only declared but not defined, leaving it to subclasses (each subclass must) define them. This also limits the creation of instances of the parent class.

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Comparison between OOP code passing and FP code passing:

OOP method: pass m2 defined by the subclass to m1 of the superclass (even if the superclass m1 does not know the specific content of m2 when it is defined, because the definition of m2 is dynamically dispatched at runtime**)**

FP's method: Advanced functions pass code, and advanced functions cannot know the specific content (for example, although g in f will be recognized as a function according to the grammar, the specific content of g will only be known at runtime ). For the defined f, there is a caller (caller such as h), and the caller h provides the definition of g and passes it to the callee f.

In general, the two are similar. They both define method m in the superclass/callee. This method m contains some other methods n (the defined methods will only be known at runtime), and the definition of this n ( Additional information/code that needs to be passed) is provided by the subclass or the caller.

This is a common programming technique.

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)

Finally, we discuss C++ without interfaces. Interfaces can be implemented through classes and all abstract methods (pure virtual functions), and then interface implementation is implemented through class inheritance (multiple inheritance).

Programming Languages ​​PartC Week2 study notes - OOP (object-oriented) vs FD (functional)
This chapter also introduces object-oriented programming and compares functional programming. Overall, I benefit a lot.