Why in Java inherit is harmful

xiaoxiao2021-03-11  176

Most good designers are like avoiding plague to avoid using implementation inheritance (Extends). Actually 80% of the code should be written entirely in Interfaces, not through Extends. The "Java Design Mode" is explained in detail how to inherit the inheritance inheritance. This article describes why the designer makes this. Extends is harmful; perhaps the level of Charles Manson is not, but it is poor enough to be avoided at any time. "Java Design Mode" has a large part of the discussion with interfab inheritance instead of reality. A good designer is in his code, mostly with Interface instead of a specific base class. This article discusses why designers will choose this, and also introduce some interfab-based programming basis. Interface and class (Class)  once, I participated in a Java user group meeting. In the meeting, Jams Gosling (the father of Java) made a sponsor speech. In that unforgettable Q & A section, someone asked him: "If you re-construct java, what do you want to change?". "I want to abandon classes" he answered. After laughing, it explains that the real problem is not due to the Class itself, but implements inheritance (Extends). Interface inheritance (IMPLEMENTS relationship) is better. You should avoid inheritance as much as possible. Losing flexibility Why should you avoid inheritance? The first question is that the specific class name will be fixed to a particular implementation, and the change in the bottom layer has increased unnecessary difficulties. In the current agile programming method, the core is the concept of parallel design and development. Before you detail, you start programming. This technology is different from the form of traditional methods --- Traditional way is to design before the start of the coding - but many successful projects have proven that you can develop high quality code faster, relative to traditional according to the class method. However, the core developed in parallel is the flexibility. You have to write your code in a way so that the latest demand can be used as much as possible to merge into existing code. Slosing the features that you may need, you only need to achieve the characteristics you have clearly needed, and moderately vary. If you don't have this flexible, parallel development, it is impossible. The programming of Inteface is the core of the flexible structure. To illustrate why, let us see what will happen when using them. Consider the following code:

f ()

{LinkedList List = new linkedList ();

// ...

g (list);

}

LINKEDLIST LIST

{

List.add (...);

G2 (List)

}

Now, suppose a requirement for fast queries is proposed so that this LinkedList cannot be resolved. You need to use HashSet instead of it. In an existing code, the change is not able to locate because you need to modify f () also need to modify G () (it comes with a LinkedList parameter), and there is also a g () to pass the list to any code. Rewrive code below:

f ()

{Collection List = New LinkedList ();

// ...

g (list);

}

G (Collection List)

{

List.add (...);

G2 (List)

}

This modification of Linked List is HASH, which may only use new hashset () to replace New LinkedList (). that's it. There are no other places that need to be modified. As another example, compare the following two codes:

f ()

{Collection c = new hashset (); // ...

g (c);

}

G (Collection C)

{

Iterator i = c.iterator (); i.hasnext ())

Do_something_with (i.next ());

}

with

F2 ()

{Collection c = new hashset ();

// ...

G2 (c.iterator ());

}

G2 (Iterator i)

{while (I.hasnext ())

Do_something_with (i.next ());

}

The G2 () method is now able to traverse the collection of Collection, just like you can get the key value pair you get in Map. In fact, you can write Iterator, which produces data, replaces a collection of Collection. You can write to iterator, it gets information from the test framework or file. This will have huge flexibility. Coupling For inheritance, a more critical issue is coupling - the irritated dependence is part of the program's dependence on another part. The global variable provides a classic example, which proves why strong coupling will cause trouble. For example, if you change the type of global variable, all functions of this variable may have been affected, so all of these code is checked, change, and retest. Moreover, all functions used to this variable are coupled to each other by this variable. That is, if a variable value is changed when it is difficult to use, a function may affect another function of another function. This issue is significantly hidden in multi-threaded programs. As a designer, you should work hard to minimize coupling relationships. You cannot eliminate coupling, because the method call from a class object to another class is a loosely coupled form. You can't have a program that doesn't have any coupling. However, you can minimize certain coupling by complying with the OO rules (most importantly, an object's implementation should be completely hidden in using his object). For example, an instance variable of an object (not a constant member domain) should always be private. I mean a period of time, no exception, constant. (You can occasionally use the protected method, but protected instance variable is awkward) The same reason you should not need Get / Set function - they are just a domain public just make people feel too complicated (although return to modification) Object rather than the basic type value is in some cases, in that case, the returned object class is a key abstraction when designing. Here, I am not angry. In my own work, I found a direct interrelationship between my OO method, fast code development and easy code implementation. Whenever I violate the center of OO principles, if I have hidden, I've rewritten that code (generally because the code is unmodable). I have no time to rewrite the code, so I follow those rules. I am concerned with the full use - I am not interested in clean reasons. Fragile base class problem now, let us apply coupling concept to inherit. In an inherited inheritance in Extends, the derived class is very close and base class coupled, and this tight connection is undesirable. The designer has applied nickname "fragile basic class problem" to describe this behavior. The basic class is considered to be fragile, because you modify the base class in the event of a safe situation, but when you inherit the class, new behaviors may cause the derived class to disorder. You can't see the change of the base class by simple approach to the base class is secure; but you must also see (and test) all derived classes. Moreover, you must check all the code, which is also used in the base class and derived class object, because this code may be broken by the new behavior. A simple change for basic classes can cause the entire program to be unacceptable. Let us check the problem of fragile base classes and base classes. The following class extended Java's ArrayList class enables it to run like a Stack: Class Stack Extends ArrayList

{private int stack_point = 0;

Public void push (Object Article)

{add (stack_pointer , article);

}

Public Object Pop () {Return Remove (--stack_pointer);

}

Public void push_many (Object [] articles)

{for (int i = 0; i

Push (articles [i]);

}

}

Even a simple class like this is also a problem. Reflections When a user balances inherits and uses the Clear () method of arraylist:

Stack a_stack = new stack ();

A_stack.push ("1");

A_stack.push ("2");

a_stack.clear ();

This code is successfully compiled, but because the base class does not know about the STACK pointer stack, this STACK object is currently in an undefined state. The next place puts the new item in the location of the index 2 for the PUSH () call. (The current value of Stack_Pointer), so Stack effectively has three elements - two of the following are garbage. (Java's Stack class is there, don't use it). The solution to this hateful inheritance method is to override all ArrayList methods for Stack, which can modify the status of the array, so override the correct operation Stack pointer or throw an exception. (REMOVERANGE () method For a good candidate approach to throwing an exception). This method has two shortcomings. First, if you override all things, this base class should be real is an interface instead of a Class. If you don't have to inherit methods, there is no such thing in the implementation of inheritance. Second, more importantly, you cannot let a Stack support all ArrayList methods. For example, there is no role in removerange (). The only reasonable way to achieve useless methods is to throw it an exception because it should never be called. This method effectively makes compilation errors run error. The bad way is that if the method is only not defined, the compiler will output a method that has not found an error found. If the method exists, but throw an exception, you can only find call errors when you really run in the program. A better solution for this base class problem is to encapsulate data structures instead of inheritance. This is new and improved version of Stack:

Class Stack

{

Private int stack_point = 0;

Private arraylist the_data = new arraylist ();

Public void push (Object Article)

{

THE_DATA.ADD (stack_poniter , article);

}

Public Object Pop ()

{

Return the_data.remove (--stack_pointer);

}

Public void push_many (Object [] articles)

{

For (int i = 0; i

Push (articles [i]);

}

}

So far, it is always good, but considering the fragile base class problem, we say that you want to create a variable in Stack, use it to track the maximum stack size in a period of time. A possible implementation may be like this:

Class Monitorable_stack Extends Stack

{

PRIVATE INT high_water_mark = 0;

Private int current_size; public void push (Object Article)

{

IF ( current_size> high_water_mark)

HIGH_WATER_MARK = CURRENT_SIZE;

Super.push (article);

}

Publish Object Pop ()

{

--current_size;

Return super.pop ();

}

Public int maximum_size_so_far ()

{

RETURN HIGH_WATER_MARK;

}

}

This new class is working very well, at least for a while. Unfortunately, this code discovered a fact, push_many () runs by calling push (). First, this detail doesn't look a bad choice. It simplifies the code, and you can get the derived class version of Push (), even when Monitorable_stack is accessed through the reference of Stack, so that high_water_mark can be updated correctly.

转载请注明原文地址:https://www.9cbs.com/read-128961.html

New Post(0)