الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

تخفيض مستوى التمثيل من الصف BufferedImage

مغلق
بدأه MMSs في 14 أغسطس 2005 · 15 رد · 1,060 مشاهدة · في JavaSE
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكم،

سؤال جديد في سلسلة الأسئلة الاختصاصية في مجال معالجة الصور في جافا!

أنشأت صف جديد مشتق من الصف BufferedImage يدعى WordImage لإضافة بعض الطرق المختصة بمعالجة الكلمات في صورة.

المهم؛ بعد إنشاء غرض من الصف WordImage أحاول أن أجعله يؤشر إلى صورة بعد قراءتها بالطريقة read(File) الموجودة في الصف ImaegIO بالطريقة التالية:

WordImage img = (WordImage) ImageIO.read(new File("image.jpg");

كما لاحظتم أقوم بتخفيض مستوى تمثيل الغرض العائد من الطريقة read الذي هو من النمط BufferedImage إلى النمط WordImage بشكل صريح لأن هذا لا يتم بشكل ضمني. ولكن بالرغم من ذلك العملية لم تنجح وتقول رسالة الخطأ وجود خطأ في الـ casting، الخطأ من النمط ClassCastException.

فما الحل؟؟؟

هذا تعريف الصف WordImage مع المشيد للتسهيل المساعدة، ولكم الشكر مسبقاً:

import java.awt.image.*;

public class WordImage extends BufferedImage {
  public WordImage(int width, int height) {
    super(width, height, BufferedImage.TYPE_4BYTE_ABGR);
    for (int i = 0; i < this.getWidth(); i++)
      for (int j = 0; j < this.getHeight(); j++)
        this.setRGB(i, j, 0xffffff);
  }

  //too many methods not written here to save time and because they are not essential in this problem.
}

تم تعديل هذه المشاركة بواسطة MMSs في 14 أغسطس 2005 في 18:14

#2

المشكلة ليست في فئتك، لكنها في فهمك للتمثيل.

الطريقة read ترجع لك BufferedImage .... و BufferedImage ليست بالضرورة WordImage وهذا ماحصل!!

ليكن لدي طريقة ترجع لي قلما. :

public Pen getPen();

هذه الطريقة قد ترجع قلم حبر أو قلم رصاص أو قلم سائل..... فلو أرجعت لك فلم رصاص، فلن تستطيع تمثيله كقلم حبر،، لأنه ليس كذلك!!

التمثيل السفلي يكون في هذا المثال: إذا كنت تعرف أن الطريقة السابقة سترجع لك قلم حبر (أي أنك متأكد أنه قلم حبر) فيمكنك كتابة ذلك:

class Pen extends Object{}
class BallPen extends Pen{}
class Pencel extends Pen{}
class LiquidPen extends Pen{}

class Test{
static void main(){

Pen pen = getPen(); //This will Return a Pen, but we dont know what kind!!! maybe a pencel , maybe  liquid!!

if ( pen instanceof Pencil) {
Pencil pencil = (Pencil) pen;  // we now know it is a pencil!!
System.out.println("It is a pencel");
}
if ( pen instanceof BallPen) {
BallPen pencil = (BallPen) pen;  // we now know it is a BallPen!!
System.out.println("It is a ballPen");
}

ماذا يعني كل هذا؟؟ يعني هذا أن الطريقة read سترجع لك BufferedReader وليس بالضرورة WordImage ولهذا يظهر الخطأ أن هذا (الشيء) ليس WordImage وبالتالي ينتج خطا ClassCastException معناه تمثيل فئوي خاطئ!!!

أرجو أن يكون هذا مفيدا...

راجع دروس الجافا لفهم هذه الأمور فهذي برمجة شيئية التوجه!

بالتوفيق!!

#3

شكراً لك كثيراً أخي أبو مازن على هذا الإيضاح،

الحقيقة أن هذه الفكرة قد غابت عن ذهني كلياً، ولكن ما الحل الآن، فالطريقة ImageIO.read() لن تعيد يوماً غرضاً غير الغرض BufferedImage وبالتالي العبارة الشرطية التالية

BufferedImage img = ImageIO.read(new File("image.jpg"));
if (img instanceof WordImage) {
  //my code
}

لن تعمل نهائياً، لأن الشرط سوف يكون false دوماً، فالغرض img من النمط BufferedImage وأسند إليه غرض من النمط BufferedImage فهو ليس من النمط WordImage على الإطلاق. فما الحل؟؟

أعتقد أنه يتوجب علي إنشاء صف جديد مشتق من الصف ImageIO أعيد فيه تعريف الطريقة read لتعيد غرضاً من النمط WordImage. ولكن بالطبع لن أستطيع فالصف ImageIO هو صف نهائي final لذلك لا يمكن الاشتقاق منه.

إذاً ما الحل؟؟ أعتقد أني أقترب منه ولكني لا أزال لم أجده..

#4

أعتقد أنك تريد أن تصنع شيئا مفيدا، لكنك أخطأت في الطريقة... السؤال بالضبط: ماذا تريد أن تفعل؟؟؟ ما وظيفة WordImage??? ما هو برنامجك؟؟

إذا ساعدتني في فهم ما تريد فعله قد يساعدك الأعضاء بشكل أفضل!!

ملاحظة.. حتى لو كان ImageIO غير final لما استطعت أن تغير read لأنها static!!!

#5

إذا كانت الطريقة static فلماذا لا أستطيع إعادة صياغتها، لماذا لا أستطيع أن أعمل لها Overriding؟ لم أفهم هذا.. فالشيفرة التالية مثلاً ليس فيها خطأ :

class Parent {
  static void testMethod() {}
}

class Child extends Parent {
  static void testMethod() {}
}

على كل الأحوال لا أريد أن أخرج عن الموضوع الرئيسي هنا (يمكن أن نفتح موضوع جديد لنناقش لماذا override الـ static method غير ممكن).

بالنسبة لسؤالك، أعتقد أن اسم الصف WordImage قد أوحى لك بعمله، وهذا ما دفع بك لتسأل لتأكد تخمينك، وأنا أقول لك أن تخمينك صحيح. فالصف WordImage هو صف خاص لمعالجة الصور على أنها كلمات، يحوي على عدد من الطرق التي تعالج الصورة لتحليلها واستخراج البقع المضيئة (المكتوبة) ومعالجتها لتحسينها وفرزها، وبالتالي يعطي صفوف غيره إمكانية العمل للتعرف على الحروف المكتوبة.

وعلى كل الأحوال، قد اهتديت إلى طريقة أخرى في تعريف الصف WordImage وطرقه، حتى أني أستطيع أن أجعله غير مشتق من BufferedImage، وبهذه الطريقة أكون قد تخلصت من المشكلة التي أنا فيها الآن. ولكن تبقى للفائدة العامة إيجاد حل لهذه المشكلة أمر مفيد فلعلنا نواجهها مرة أخرى بطريقة مختلفة.

#6
public class ImageWordReader{
BufferedImage image;
public ImageWordReader(BufferedImage image){
this.image=image;
}
public String getWord(){
//Your code here!!
return newString;
}
}

بصراحة لا أدري كيف ستفعلها،، لكن هذه طريقة التصميم التي أقترحها!! وليس WordImage extends BufferedImage!!

أما إعادة كتابة الطرق الثابتة، فهو غير منطقي، أو بالأحرى ليس شيئي التوجه!! عموما جرب هذا...

class Parent {
 static void testMethod() {
System.out.println("Parent Static Method");

}
}

class Child extends Parent {
 static void testMethod() {
System.out.println("Child Static Method.");
}
public static void main(String[]args){
Parent.testMethod();
Child.testMethod();
}
}
#7

أو بصراحة هذا تصميم أفضل:

public class MyImageUtilities{

public static String getWord(BufferedImage image){
//code;
return newString;
}
}
#8

بالنسبة لإعادة تعريف static method فقد تم بنجاح بمثالك. وكون إعادة تعريف static method يخرج عن نطاق البرمجة غرضية التوجه فهذا يعني أنه لا ينصح به، ولكنه ليس ممنوعاً (أو بمعنى أصح لا يجعل مترجم Java يرسل رسالة خطأ عند وجوده).

بالنسبة لتصميم البرنامج، كان في البداية تقريباً كما في تصميمك الثاني حيث كل الطرق سكونية ويتم استدعاء الطريقة المطلوبة من خارج الكلاس، ثم غيرته ليكون كما في التصميم الأول الصف يحوي غرض من النمط BufferedImage وتتم عملية المعالجة على هذا الغرض. ولكن بعد التفكير في التصميم الأخير رأيت أن أجعل الصف WordImage نفسه يؤشر إلى الصورة التي أعالجها لهذا فكرت في اشتقاقه من الصف BufferedImage. ولكن كما لاحظت قد فشل هذا الأمر معي، ولهذا قررت الآن أن أعيد تصميم البرنامج ليصبح كما في تصميمك الأول، مع العلم بأن الطريقة التي سوف تعيد الكلمة getWord ليست موجودة هنا، ليست في هذا الصف، إنما هذا الصف لمعالجة الصورة فقط.

#9

على فكرة، أرى أن أفضل تصميم غرضي التوجه هو التصميم الذي أعتمده أخيراً، فما رأيك؟؟!

#10
اقتباس
على فكرة، أرى أن أفضل تصميم غرضي التوجه هو التصميم الذي أعتمده أخيراً، فما رأيك؟؟!

أي أخير؟ أرجو التوضيح... لقد ضعت!!

سأقول ما فهمته منك: تريد إنشاء برنامج معين يقرأ الكلام المكتوب في الصور. تريد أن تعطي البرنامج صورة ويقوم بدوره بإعطائك نتيجة القراءة.

--------------------

اقتباس
بالنسبة لإعادة تعريف static method فقد تم بنجاح بمثالك. وكون إعادة تعريف static method يخرج عن نطاق البرمجة غرضية التوجه فهذا يعني أنه لا ينصح به، ولكنه ليس ممنوعاً (أو بمعنى أصح لا يجعل مترجم Java يرسل رسالة خطأ عند وجوده).

أدرجت هذا المثال حتى ترى عدم إمكانية إعادة كتابة الطريقة الثابتة، وليس العكس!!

إعادة الكتابة معناها، أنك لا تستطيع مناداة القديم في Parent إذا كان عندك الجديد من Child... فإذا أعدت كتابة طريقة (غير ثابتة) في Child وأردت بعد ذلك استخدام هذه الطريقة مع Child لكنك تريد التنفيذ القديم فلن تستطيع ذلك،،، أما إذا كانت الطريقة ثابتة، فإعادة كتابتها لا معنى لها،، فأنت لا زلت تستطيع استخدام القديم مع الجديد.

ولا أقصد بإعادة الكتابة إمكانية كتابة طريقة جديدة بنفس اسم دالة سابقة في الفئة الأم! يمكنك عمل هذا، لكنك تنتج طريقة جديدة بجانب القديمة ولا تمسح القديمة. (يمكنك الوصول من الخارج إلى الطريقة القديمة!

انظر إلى الفئة Test عندي كائن من الفئة Child،،، أستطيع أن أستخدم Parent.staticMethod و Child.staticMethod مع كائني الصغير Child!!وكلهم يعطي نتيجة مختلفة، لأنهما static،،، لكني لا أستطيع الحصول على التنفيذ nonStaticMethod الموجود في Parent لأني أعدت كتابتها!! c.nonStaticMethod !!

أرجو أن يكون هذا واضحا!!

class Parent{
	String name;
	int number;
	public Parent(String name, int number){
  this.name=name;
  this.number=number;
	}
	public static void staticMethod(Parent parent){
  System.out.print ("Printing Everything:\t");
  System.out.println (parent.name+"\t"+parent.number);
	}
	public void nonStaticMethod(){
  System.out.println (name+"\t"+number);
	}
}
class Child extends Parent{
	public Child(String name){
  super(name, 10);
	}
	public void nonStaticMethod(){
  System.out.println (name);
	}
	public static void staticMethod(Parent parent){
  System.out.print ("Printing name only:\t");
  System.out.println (parent.name);
	}
}
class Test{
	public static void main(String[]arfs){
  Parent p=new Parent("Parent", 30);
  Child c=new Child("Child");
  System.out.println ("Trying to call non static method...");
  p.nonStaticMethod();
  //c.nonStaticMethod you dont have the ability to call the parent non static method
  //becasue it is overriden
  c.nonStaticMethod();
  System.out.println ("Non static method can be overriden.");

  System.out.println ("Trying to print data using static method");
  Parent.staticMethod(p);//
  Parent.staticMethod(c);//we still can call the parent method
  Child.staticMethod(p);//we are calling the child method
  Child.staticMethod(c);
  
  //you can see that parent static methods can be called even though 
  //they are overriden!!!



	}
}
#11

أنا آسف لأني خلطت الأمور بعض الشيء!

كنت أقصد في التصميم الذي أعتمده أخيراً التصميم الذي ذكرته في بهذا الشكل:

public class ImageWordReader{
BufferedImage image;
public ImageWordReader(BufferedImage image){
this.image=image;
}
public String getWord(){
//Your code here!!
return newString;
}
}

وما فهمته صحيح، فما أصنعه بالضبط هو OCR.

اقتباس
أدرجت هذا المثال حتى ترى عدم إمكانية إعادة كتابة الطريقة الثابتة، وليس العكس!!

اسمح لي بتعديل مثالك إلى الشكل الآتي:

class Parent {
  String name;
  int number;
  public Parent(String name, int number) {
    this.name = name;
    this.number = number;
  }

  public static void staticMethod(Parent parent) {
    System.out.println(parent.name + "\t" + parent.number);
  }
  public void nonStaticMethod(Parent parent) {
    System.out.println(parent.name + "\t" + parent.number);
  }
  public static void notOverriddenStaticMethod(Parent parent) {
    System.out.println(parent.name + "\t" + parent.number);
  }
}

class Child
    extends Parent {
  public Child(String name) {
    super(name, 10);
  }

  public static void staticMethod(Parent parent) {
    System.out.println(parent.name);
  }
  public void nonStaticMethod(Parent parent) {
    System.out.println(parent.name );
  }
}

class Test {
  public static void main(String[] arfs) {
    Parent p = new Parent("Parent", 30);
    Child c = new Child("Child");
    System.out.println("calling non static method...");
    p.nonStaticMethod(p);
    p.nonStaticMethod(c);
    c.nonStaticMethod(p);
    c.nonStaticMethod(c);
    System.out.println("calling static method...");
    p.staticMethod(p); 
    p.staticMethod(c);
    c.staticMethod(p); 
    c.staticMethod(c);
    System.out.println("calling static method not overridden");
    p.notOverriddenStaticMethod(p);
    p.notOverriddenStaticMethod(c);
    c.notOverriddenStaticMethod(p);
    c.notOverriddenStaticMethod(c);
  }
}

نفذ المثال السابق ولاحظ معي أن الطريقة السكونية تورٌّث وتعاد صياغتها ولا يمكن الوصول إلى الطريقة السكونية للصف الأب إلا عن طريق الصف الأب أو غرض من الصف الأب إنما لا يمكن الوصول لها عن طريق الصف الابن أو غرض من الصف الابن إلا إذا لم تعاد صياغتها في الصف الابن.

أتمنى أن يكون كلامي واضحاً، وهدفنا هو الفائدة وتبادل الخبرات. وإن كانت وجهة نظري خطأ أحب أن تصححها لي.

#12

التصميم الذي ذكرته ممتاز،، وطبعا يعتمد على برنامجك ككل،، بالتوفيق!!

:)

p.staticMethod(p); 
   p.staticMethod(c);
   c.staticMethod(p); 
   c.staticMethod(c);

يمكنك استعمال مؤشر لمناداة الطرق الثابتة،، لكن هذا مضلل..

استخدم اسم الفئة مع الطرق الثابتة:

استخدم Parent.staticMethod

لاحظ معي أنك لا تستطيع أبدا أن تمسح الشفرة Parent.staticMethod حتى لو أعدت كتابتها في Child.. هذا ما نقصد عندما نقول أننا أعدنا كتابة الشفرة.. أما c.nonStaticMethod لاتستطيع أن تصل إلى الشفرة المكتوبة في Parent.nonStaticMethod.. هذا ما أريد أن أقوله كما في مثالي السابق!!

باستعمال Child هل تستطيع أن تخفي عني Parent.staticMethod??

لا أعتقد ذلك،، لكنك تستطيع أن تخفي عن Parent.nonStaticMethod.. لأنك تعيد كتابتها...في Child..

#13

كلامك تمام، من المستحيل إخفاء طريقة سكونية عندما تتم مناداتها من صفها، ولكن ما أردت قوله هو أن الطريقة السكونية تعامل تماماً كما تعامل الطريقة الغير سكونية بالإضافة إلى إمكانية الوصول إليها عن طريق الصف الذي يحويها.

#14

نسيت أن أقول لك بأني معك في كون عملية إعادة صياغة الطرق السكونية لا يمت إلى البرمجة غرضية التوجه بصلة.

#15

أحب أن أرى البرنامج عند انتهائك منه..

بالتوفيق!

#16

السلام عليكم،

ربما أصبح الموضوع قديم بعض الشيء، ولكني تذكرت أمراً بخصوص مسألة إعادة الصياغة. حتى تمنع إمكانية إعادة صياغة طريقة ما يجب أن تعطيها الصفة final، فالمثال التالي مثالاً خاطئ لأنه يعيد تعريف طريقة في الصف الأب موصوفة بـ final (وطبعاً لا يهم مهما كان للطريقة صفات أخرى ، static مثلاً) :

class Parent {
  public final finalMethod() {
  }
}

class Child extends Parent {
  public final finalMethod() {
  }
}

أنا أكيد أنك على علمٍ بهذا، ولكني أحببت ذكر هذا الموضوع عند تذكري له للفائدة العامة.

تحياتي....

هذا الموضوع مغلق.

مواضيع مشابهة