¿El desarrollo de Java generalmente implica más subclases que C # /. NET?

34

Recientemente comencé a mirar el desarrollo de Android. Esto me ha traído de vuelta al mundo del desarrollo de software Java. La última vez que trabajé con Java, lo admito, no entendía OOP tanto como (creo) ahora.

Habiendo usado principalmente C # en mi carrera, noto una sorprendente diferencia en cómo se usa la herencia Java y C #.

En C # parecía que la herencia podría evitarse en la mayoría de las situaciones. La tarea en cuestión generalmente se puede lograr mediante el uso de clases concretas de .NET Framework.

En Java, por lo que estoy recopilando de muestras de código, parece que el marco de Java proporciona muchas interfaces o clases abstractas que el desarrollador debe implementar / extender.

Esto parece ser una gran diferencia para reducirlo al estilo. ¿Cuál es el razonamiento detrás de esto? Siento que no escribiré código Java limpio hasta que entienda esto.

Además, ¿esto se limita solo al SDK de Android o es un enfoque de Java para OOP?

O dicho de otra manera,

¿Qué tiene el diseño de estos dos idiomas que (parece alentar) más o menos el uso de la herencia que el otro?

Si los idiomas tratan la herencia de forma idéntica, y suponiendo que mi observación sea válida, significa que esto está relacionado con el diseño de los marcos / bibliotecas y no con los idiomas. ¿Cuál sería la motivación para este tipo de diseño?

MetaFight
fuente
1
Es verdad. Java tiene demasiadas interfaces. Me encantaría saber por qué este comportamiento de desarrollador es común para un lenguaje específico. Veo que esto sucede más a menudo en proyectos Java que en cualquier otro. Tiene que haber una razón para esto y no solo una opinión.
Reactgular
1
@MathewFoscarini es solo tu opinión . En los proyectos Java en los que participé, los desarrolladores se inclinaban por evitar la herencia como una plaga. Nada autoritario aquí, solo mi opinión . ¿Quieres sondear ?
mosquito
2
Casi nunca necesitas derivar una clase de otra en Java. En su lugar, puede implementar interfaces para lograr el polimorfismo e implementar objetos compuestos y proxy que el método llama para lograr una funcionalidad sombreada.
DwB
1
@ThinkingMedia En general, Java está invadido por fanáticos / puristas de OSS. Los principios académicos son la principal preocupación. Los desarrolladores de .Net son pragmáticos preocupados por hacer el trabajo. Valoran el código de trabajo más que el código más limpio.
Andy

Respuestas:

31

Esto parece ser una gran diferencia para reducirlo al estilo. ¿Cuál es el razonamiento detrás de esto?

Tengo entendido que en gran medida es simplemente una decisión estilística. Bueno, quizás no sea el estilo, sino los modismos del lenguaje / entorno. Los desarrolladores de la biblioteca estándar de Java siguieron un conjunto de pautas de diseño y los desarrolladores de .NET otro (aunque tenían la capacidad de ver cómo funcionaba el enfoque de Java).

Hay muy poco en los idiomas actuales para alentar o disuadir la herencia. Solo dos cosas me parecen relevantes:

  1. .NET introdujo genéricos más temprano en su vida, antes de que se implementara demasiado código no genérico. La alternativa es mucha herencia para escribir cosas especializadas.

  2. Un cambio mayor fue que .NET apoyó delegados. En Java, está atascado con la herencia (anónima) para proporcionar la funcionalidad variable más básica. Esto conduce a una diferencia relativamente grande en la forma en que el código está diseñado para aprovechar delegados o para evitar las estructuras de herencia incómodas necesarias para hacerlo en Java.

Telastyn
fuente
3
Creo que esta respuesta ofrece una explicación muy plausible. Especialmente el punto sobre la herencia anónima en lugar de los delegados. Lo he observado mucho. Gracias.
MetaFight
3
No olvide los tipos anónimos y los árboles de sintaxis / expresión lambda que se introdujeron en .NET 3.5. Y, por supuesto, optar por la escritura dinámica en .NET 4. Ahora es casi un lenguaje de paradigma mixto, no estrictamente orientado a objetos.
Aaronaught
Sí, en mi experiencia, habiendo usado ambos (incluso en proyectos mixtos donde las mismas personas usan ambos idiomas), las personas tienden a preferir usar un mayor número de clases más pequeñas al codificar Java, un número menor de clases más grandes (tendencia hacia las clases de Dios) cuando usando C #. Después de crear prototipos deliberadamente de la misma cosa usando el mismo estilo en ambos, terminas con un número similar de clases (usando clases parciales, es probable que termines con más archivos de código incluso cuando usas C #).
Jwenting
6

Estos son idiomas muy diferentes, particularmente en esta área. Digamos que tienes una clase. En este caso, lo haremos un control de usuario, algo así como un cuadro de texto. Llámalo UIControl. Ahora queremos poner esto en otra clase. En este caso, como estamos usando una IU para nuestro ejemplo, la llamaremos la clase CleverPanel. Nuestra instancia de CleverPanel querrá saber sobre las cosas que le suceden a su instancia de UIControl por varias razones. ¿Como hacer esto?

En C #, el enfoque básico es verificar varios eventos, configurando métodos que se ejecutarán cuando se active cada evento interesante. En Java, que carece de eventos, la solución habitual es pasar un objeto con varios métodos de manejo de "eventos" a un método UIControl:

boolean  stillNeedIt =  ... ;
uiControl.whenSomethingHappens( new DoSomething()  {
    public void resized( Rectangle r )  { ... }
    public boolean canICloseNow()  { return !stillNeedIt; }
    public void closed()  { ... }
    ...
} );

Hasta ahora, la diferencia entre C # y Java no es profunda. Sin embargo, tenemos la interfaz DoSomething que no se necesita en C #. Además, esta interfaz puede incluir muchos métodos que no son necesarios la mayor parte del tiempo. En C #, simplemente no manejamos ese evento. En Java, creamos una clase que proporciona una implementación nula para todos los métodos de interfaz, DoSomethingAdapter. Ahora reemplazamos DoSomething con DoSomethingAdapter y no necesitamos escribir ningún método para una compilación limpia. Terminamos anulando los métodos que necesitamos para que el programa funcione correctamente. Así que terminamos necesitando una interfaz y usando herencia en Java para que coincida con lo que hicimos con los eventos en C #.

Este es un ejemplo, no una discusión exhaustiva, pero brinda los conceptos básicos de por qué hay tanta herencia en Java en comparación con C #.

Ahora, ¿por qué Java funciona de esta manera? Flexibilidad. El objeto pasado a whenSomethingHappens podría haberse pasado completamente a CleverPanel desde otro lugar. Podría ser algo que varias instancias de CleverPanel deberían pasar a sus objetos similares a UIControl para ayudar a un objeto CleverWindow en alguna parte. O el UIControl podría pasarlo a uno de sus componentes.

Además, en lugar de un adaptador, puede haber una implementación DoSomething en algún lugar que tenga miles de líneas de código detrás. Podríamos crear una nueva instancia de eso y pasarlo. Es posible que debamos anular un método. Un truco común en Java es tener una clase grande con un método como:

public class BigClass implements DoSomething  {
    ...many long methods...
    protected int getDiameter()  { return 5; }
}

Luego en CleverlPanel:

uiControl.whenSomethingHappens( new BigClass()  {
    @Override
    public int getDiameter()  { return UIPanel.currentDiameter; }
} );

La plataforma Java de código abierto hace mucho de esto, lo que tiende a impulsar a los programadores a hacer más, tanto porque lo siguen como un ejemplo y simplemente para usarlo. Creo que el diseño básico del lenguaje está detrás del diseño del marco de Sun y detrás del programador de Java que usa las técnicas cuando no usa el marco.

Es realmente fácil crear una clase sobre la marcha en Java. La clase, anónima o con nombre, solo necesita ser referenciada en un pequeño bloque de código enterrado profundamente en un método. Se puede crear completamente nuevo o mediante ligeras modificaciones a una clase existente muy grande. (Y la clase existente puede ser de nivel superior en su propio archivo, o anidada en una clase de nivel superior, o definida solo dentro de un solo bloque de código). La nueva instancia de clase puede tener acceso completo a todos los datos del objeto de creación. Y la nueva instancia se puede pasar y usar en todo el programa, representando el objeto que la creó.

(Por otro lado, tenga en cuenta que un gran uso de la herencia aquí, como en otros lugares de Java, es simplemente para fines SECOS. Permite que diferentes clases reutilicen el mismo código. Tenga en cuenta también la facilidad de herencia en Java que fomenta esto. )

Nuevamente, esta no es una discusión exhaustiva; Solo estoy rascando la superficie aquí. Pero sí, hay una sorprendente diferencia en cómo se usa la herencia entre Java y C #. Son, a este respecto, idiomas muy diferentes. No es tu imaginación.

RalphChapin
fuente
Nota: Puede evitar tener que proporcionar métodos que no hacen nada extendiendo una clase abstracta llena de implementaciones vacías. De esta manera, puede anular selectivamente los métodos que hacen algo. Si bien no es tan feo, significa otro nivel de herencia.
Peter Lawrey
-2

No hay absolutamente ninguna diferencia en la forma en que se maneja la herencia entre Java y C #. Cuando realmente usará la herencia o la composición es definitivamente una decisión de diseño y de ninguna manera es algo que Java o C # alienta o desalienta. Sugeriría amablemente leer este artículo .

Espero haber ayudado!

Pantelis Natsiavas
fuente
55
Estás hablando de los propios idiomas, pero creo que la pregunta también es sobre las bibliotecas y el uso común.
svick
3
Pero no son caprichos en el resto de la lengua que fomenten la herencia extra en Java; ver la respuesta de
RalphChapin